인터페이스 상수의 장단점 [닫힌]


105

PHP 인터페이스는 인터페이스에서 상수 정의를 허용합니다.

interface FooBar
{
    const FOO = 1;
    const BAR = 2;
}
echo FooBar::FOO; // 1

모든 구현 클래스는 자동으로 이러한 상수를 사용할 수 있습니다.

class MyFooBar implement FooBar
{
}
echo MyFooBar::FOO; // 1

이것에 대한 내 자신의 견해는 글로벌은 모든 것이 악하다는 것 입니다. 그러나 동일한 것이 인터페이스 상수에 적용되는지 궁금합니다. 인터페이스에 대한 코딩 이 일반적으로 좋은 습관으로 간주 된다는 점을 감안할 때 인터페이스 상수를 사용하는 것이 클래스 컨텍스트 외부에서 사용할 수있는 유일한 상수입니까?

개인적인 의견과 인터페이스 상수를 사용하는지 여부가 궁금하지만 주로 답변에서 객관적인 이유를 찾고 있습니다. 나는 이것이 투표 유형 질문이되기를 원하지 않습니다. 인터페이스 상수를 사용하는 것이 유지 관리성에 어떤 영향을 미치는지 관심이 있습니다. 커플 링. 또는 단위 테스트. SOLID PHP 와 어떤 관련이 있습니까? PHP에서 Good Practice로 간주되는 코딩 원칙을 위반합니까? 당신은 아이디어를 얻습니다 ...

참고 : Java에 대한 유사한 질문 이 있는데, 이것이 나쁜 관행 인 이유를 나열한 것입니다. 그러나 Java는 PHP가 아니기 때문에 PHP 태그 내에서 다시 묻는 것이 정당하다고 느꼈습니다.


1
흠, 전에는 인터페이스에서 상수를 정의 할 필요가 없었습니다. 수업 것을 알고 그것의 가치 인터페이스를 구현하는이 클래스는 단지 서로 확장하면서, 상수를 재정의 할 수 없습니다 재정의 상수.
Charles

1
상수는 단위 테스트 가능성과 관련하여도 예측 가능한 값을 가지고 있기 때문에 나쁘지 않다고 생각합니다. 전역 변수는 변수이고 모든 것에 범위가 있기 때문에 누구나 변경할 수 있기 때문에 악의적이지만 상수는 값을 변경하지 않으므로 상수라는 용어가 사용됩니다.
mdprotacio

답변:


135

글쎄, 나는 그것이 차이로 귀결이라고 생각 좋은충분히 좋은 .

대부분의 경우 다른 패턴 (전략 또는 플라이 웨이트)을 구현하여 상수 사용을 피할 수 있지만, 개념을 표현하기 위해 6 개의 다른 클래스가 필요하지 않다고 할 수 있습니다. 요약하면 다른 상수가 얼마나 필요할까요? 즉, 인터페이스에서 상수가 제공하는 ENUM을 확장 할 필요가 있습니까? 확장 할 필요가 있다고 예견 할 수 있다면 좀 더 공식적인 패턴으로 가십시오. 그렇지 않은 경우 충분할 수 있습니다 (충분히 좋으므로 작성하고 테스트 할 코드가 적습니다). 다음은 충분히 좋고 나쁜 사용의 예입니다.

나쁜:

interface User {
    const TYPE_ADMINISTRATOR = 1;
    const TYPE_USER          = 2;
    const TYPE_GUEST         = 3;
}

충분하다:

interface HTTPRequest_1_1 {
    const TYPE_CONNECT = 'connect';
    const TYPE_DELETE  = 'delete';
    const TYPE_GET     = 'get';
    const TYPE_HEAD    = 'head';
    const TYPE_OPTIONS = 'options';
    const TYPE_POST    = 'post';
    const TYPE_PUT     = 'put';

    public function getType();
}

자, 제가 그 예를 선택한 이유는 간단합니다. User인터페이스는 사용자 유형의 정의를 열거한다. 이것은 시간이 지남에 따라 확장 될 가능성이 매우 높으며 다른 패턴에 더 적합합니다. 그러나 HTTPRequest_1_1열거 형은 RFC2616에 의해 정의되고 클래스 수명 동안 변경되지 않기 때문에 괜찮은 사용 사례입니다.

일반적으로 나는 상수와 클래스 상수의 문제를 글로벌 문제 로 보지 않습니다 . 나는 그것을 의존성 문제로 본다. 좁은 구분이지만 확실한 구분입니다. 강제되지 않는 전역 변수에서와 같은 전역 문제를 보고 소프트 전역 종속성을 만듭니다. 그러나 하드 코딩 된 클래스는 강제 종속성을 생성하므로 하드 전역 종속성이 생성됩니다. 따라서 둘 다 종속성입니다. 그러나 나는 그것이 시행되지 않았기 때문에 글로벌 이 훨씬 더 나쁘다고 생각한다. 그래서 나는 동일한 배너 아래에서 글로벌 의존성 과 함께 클래스 의존성 을 묶는 것을 좋아하지 않는다 ...

을 작성 MyClass::FOO하면의 구현 세부 정보에 하드 코딩됩니다 MyClass. 이로 인해 하드 커플 링이 생성되어 코드의 유연성이 떨어 지므로 피해야합니다. 그러나 정확히 이러한 유형의 결합을 허용하는 인터페이스가 존재합니다. 따라서 MyInterface::FOO구체적인 결합을 도입하지 않습니다. 즉, 상수를 추가하기 위해 인터페이스를 소개하지는 않습니다.

당신이 인터페이스를 사용하고, 당신이있어 그렇다면 아주 있는지 (또는 그 문제에 대한 다른 사람이) 값을 추가로 필요로하지 않습니다, 나는 정말 ... 가장 좋은 인터페이스 상수 큰 문제가 표시되지 않습니다 디자인에는 상수 나 조건부, 매직 넘버, 매직 스트링 또는 하드 코딩 된 어떤 것도 포함되지 않습니다. 그러나 사용을 고려해야하므로 개발 시간이 추가됩니다. 내 견해는 대부분의 경우 훌륭한 견고한 디자인을 만들기 위해 추가 시간을 할애 할 가치가 있다는 것입니다. 그러나 충분히 좋은 것이 실제로 받아 들여질 때가 있고 (그리고 그 차이를 이해하기 위해서는 숙련 된 개발자가 필요합니다), 그런 경우에는 괜찮습니다.

다시, 그것은 그것에 대한 나의 견해입니다 ...


4
이 경우 사용자에게 다른 패턴으로 무엇을 제안 하시겠습니까?
Jacob

@Jacob : 나는 그것을 추상화 할 것입니다. 필요에 따라 데이터베이스 테이블에서 데이터를 가져 오는 Access 클래스를 만들 것입니다. 이렇게하면 새 레벨을 추가하는 것이 새 행을 삽입하는 것만 큼 쉽습니다. 또 다른 옵션은 ENUM 클래스 집합을 만드는 것입니다 (각 권한 역할에 대해 하나의 클래스가 있음). 그런 다음 필요한 경우 클래스를 확장하여 적절한 권한을 제공 할 수 있습니다. 하지만 다른 방법도 작동합니다
ircmaxell 2011 년

3
매우 견고하고 잘 표현 된 대답! +1
Decent Dabbler 2011 년

1
공용 상수가있는 클래스에는 메서드가 없어야합니다. 데이터 구조 일 뿐이거나 객체 일 뿐이어야합니다.
OZ_ 2011 년

2
@FrederikKrautwald : 다형성으로 조건문을 피할 수 있습니다 (대부분의 경우) : 이 답변 을 확인 하고이 Clean Code 토크시청하십시오 ...
ircmaxell 2013

10

일반적으로 인터페이스에서 별도의 유형 ( "클래스")으로 상수, 특별히 열거 된 상수를 처리하는 것이 더 낫다고 생각합니다.

define(TYPE_CONNECT, 'connect');
define(TYPE_DELETE , 'delete');
define(TYPE_GET    , 'get');
define(TYPE_HEAD   , 'head');
define(TYPE_OPTIONS, 'options');
define(TYPE_POST   , 'post');
define(TYPE_PUT    , 'put');

interface IFoo
{
  function /* int */ readSomething();
  function /* void */ ExecuteSomething(/* int */ param);
}

class CBar implements IFoo
{
  function /* int */ readSomething() { ...}
  function /* void */ ExecuteSomething(/* int */ param) { ... }
}

또는 클래스를 네임 스페이스로 사용하려는 경우 :

class TypeHTTP_Enums
{
  const TYPE_CONNECT = 'connect';
  const TYPE_DELETE  = 'delete';
  const TYPE_GET     = 'get';
  const TYPE_HEAD    = 'head';
  const TYPE_OPTIONS = 'options';
  const TYPE_POST    = 'post';
  const TYPE_PUT     = 'put';
}

interface IFoo
{
  function /* int */ readSomething();
  function /* void */ ExecuteSomething(/* int */ param);
}

class CBar implements IFoo
{
  function /* int */ readSomething() { ...}
  function /* void */ ExecuteSomething(/* int */ param) { ... }
}

상수 만 사용하는 것이 아니라 제한된 값 집합이 특정 용도 ( "도메인"?)와 함께 특정 유형으로 간주되는 열거 형 값 또는 열거 형 개념을 사용하고 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.