"필요한 것만 요청"인터페이스 원리가 있습니까?


9

나는 기본적으로 "필요한 것만 요구하라"는 인터페이스를 디자인하고 소비하는 원칙을 사용하여 자랐습니다.

예를 들어 삭제할 수있는 유형이 많으면 Deletable인터페이스를 만들 것입니다 .

interface Deletable {
   void delete();
}

그런 다음 일반 클래스를 작성할 수 있습니다.

class Deleter<T extends Deletable> {
   void delete(T t) {
      t.delete();
   }
}

코드의 다른 곳에서는 항상 클라이언트 코드의 요구를 충족시킬 수있는 가장 작은 책임을 요구합니다. 따라서을 삭제 해야하는 경우 File에도을 요구 Deletable하지는 않습니다 File.

이 원리는 상식이며 이미 허용되는 이름을 가지고 있습니까? 논란의 여지가 있습니까? 그것은 교과서에서 논의됩니까?


1
느슨한 커플 링? 아니면 좁은 인터페이스?
tdammers

답변:


16

나는 이것이 로버트 마틴이 인터페이스 분리 원리 라고 부르는 것을 의미한다고 믿는다 . 인터페이스는 작고 간결한 인터페이스로 구분되므로 소비자 (클라이언트)는 관심있는 방법에 대해서만 알아야합니다. SOLID 에서 더 많은 정보를 확인할 수 있습니다 .


4

Vadim의 매우 좋은 답변을 확장하기 위해 "실제로 아님"으로 "논쟁의 여지가 있습니까?"라는 질문에 대답하겠습니다.

일반적으로 인터페이스 분리는 관련된 다양한 객체의 "변경 이유"의 전체 수를 줄임으로써 좋은 것입니다. 핵심 원리는 여러 메소드가있는 인터페이스를 변경해야 할 때 인터페이스 메소드 중 하나에 매개 변수를 추가한다고 말하면 인터페이스의 모든 소비자는 변경된 메소드를 사용하지 않더라도 적어도 다시 컴파일해야합니다. "그러나 그것은 단지 재 컴파일입니다!", 나는 당신이 말하는 것을 들었습니다. 사실 일 수도 있지만 일반적으로 바이너리를 변경하는 정도에 관계없이 일반적으로 다시 컴파일하는 것은 소프트웨어 패치의 일부로 제공되어야합니다. 이 규칙은 원래 90 년대 초반에 일반 데스크탑 워크 스테이션이 휴대 전화보다 강력하지 않았고 14.4k 보드 전화 접속이 번거 로웠으며 3.5 "1.44MB"플로피 "가 주요 이동식 미디어였습니다. 현재 3G / 4G 시대에도 무선 인터넷 사용자는 종종 데이터 계획이 제한되어 있으므로 업그레이드를 릴리스 할 때 다운로드해야 할 바이너리 수가 적을수록 좋습니다.

그러나 모든 좋은 아이디어와 마찬가지로 인터페이스 분리가 잘못 구현되면 나빠질 수 있습니다. 우선, 인터페이스를 구현하는 동안 (종속성을 채우는) 객체를 상대적으로 변경하지 않으면 서 인터페이스를 분리하면 "신 객체"반 패턴의 상대 인 "히드라"로 끝날 수 있습니다. 좁은 인터페이스에 의해 개체의 모든 지식, 강력한 특성이 종속 요소로부터 숨겨집니다. 당신은 최소한 신의 물체만큼 유지하기 어려운 많은 머리를 가진 괴물과 모든 인터페이스를 유지하는 오버 헤드로 끝납니다. 초과해서는 안되는 인터페이스는 많지 않지만 단일 객체에 구현하는 각 인터페이스는 "이 인터페이스가 객체에 기여합니까?"라는 질문에 답해야합니다.

둘째, SRP가 알려주는 내용에도 불구하고 방법 별 인터페이스가 필요하지 않을 수 있습니다. "라비올리 코드"로 끝날 수 있습니다. 한입 크기의 청크가 너무 많아서 실제로 어디에서 일어나는지 정확히 파악하기가 어렵습니다. 해당 인터페이스의 모든 현재 사용자에게 두 가지 방법이 모두 필요한 경우 두 가지 방법으로 인터페이스를 분할 할 필요가 없습니다. 종속 클래스 중 하나가 두 가지 방법 중 하나만 필요로하는 경우에도 개념적으로 응집력이 높은 경우 인터페이스를 분할하지 않는 것이 일반적으로 허용됩니다 (좋은 예는 서로 정확히 반대되는 "동의 법").

인터페이스 분리는 인터페이스에 종속 된 클래스를 기반으로해야합니다.

  • 인터페이스에 종속 된 클래스가 하나만 있으면 분리하지 마십시오. 클래스가 하나 이상의 인터페이스 메소드를 사용하지 않고 인터페이스의 유일한 소비자 인 경우 해당 메소드를 처음에 노출해서는 안됩니다.

  • 인터페이스에 종속 된 클래스가 둘 이상 있고 모든 종속자가 인터페이스의 모든 메소드를 사용하는 경우 분리하지 마십시오. 인터페이스를 변경해야하는 경우 (메소드 추가 또는 서명 변경), 현재 소비자는 모두 분리 여부에 관계없이 변경에 의해 영향을받습니다 (적어도 하나의 종속자가 필요하지 않은 메소드를 추가하는 경우 고려) 변경 사항을 새 인터페이스로 구현해야하는 경우주의하십시오 (기존 인터페이스에서 상속 가능).

  • 인터페이스에 종속 된 클래스가 둘 이상 있고 모두 동일한 메소드를 사용 하지 않는 경우 분리 후보입니다. 인터페이스의 "일관성"을보십시오. 모든 방법이 하나의 매우 구체적인 프로그래밍 목표를 추가로 달성합니까? 인터페이스 (및 구현 자)에 대해 둘 이상의 핵심 목적을 식별 할 수있는 경우 해당 라인을 따라 인터페이스를 분할하여 더 적은 "변경 이유"로 더 작은 인터페이스를 작성하십시오.


코드가 인터페이스의 정확한 조합을 지정할 수있는 OOP 언어 / 시스템을 사용하는 경우 인터페이스 분리가 훌륭하고 멋질 수 있다는 점도 주목할 가치가 있습니다. "IFoo 및 IBar를 구현하지만 공통점이없는 항목"모음을 지정하는 방법.
supercat

제네릭 형식 매개 변수는 여러 인터페이스 구현을 포함한 조건으로 정의 할 수 있지만 정적 형식이 필요한 식은 일반적으로 둘 이상의 지정을 지원할 수 없습니다. 정적 유형이 IFoo와 IBar를 모두 구현해야하고 두 인터페이스를 모두 제어 IBaz : IFoo, IBar해야하는 경우 대신 구현 하고 요구하는 것이 좋습니다.
KeithS

클라이언트 코드에 IFooand 로 사용할 수있는 것이 필요할 경우 IBar컴포지트를 정의하는 IFooBar것이 좋은 아이디어 일 수 있지만 인터페이스가 세분화되어 있으면 수십 가지의 고유 한 인터페이스 유형을 요구하기 쉽습니다. 열거, 보고서 수, n 번째 요소 읽기, n 번째 요소 쓰기, n 번째 요소 앞에 삽입, n 번째 요소 삭제, 새 항목 (새 공간의 더 큰 콜렉션 및 리턴 색인) 및 추가 기능 콜렉션이있을 수 있습니다. 9 가지 방법 : ECRWIDNA. 아마도 많은 다른 조합을 자연스럽게 지원하는 수십 가지 유형을 설명 할 수있을 것입니다.
supercat

예를 들어 어레이는 ECRW를 지원합니다. 배열 목록은 ECRWIDNA를 지원합니다. 스레드 안전 목록은 ECRWNA를 지원할 수 있습니다 (A는 일반적으로 목록을 미리 채우는 데 유용하지만). 읽기 전용 배열 래퍼는 ECR을 지원할 수 있습니다. 공변량리스트 인터페이스는 ECRD를 지원할 수 있습니다. 비 일반 인터페이스는 유형 안전 지원 C 또는 CD를 제공 할 수 있습니다. 스왑이 옵션 인 경우 일부 유형은 CS를 지원하지만 D (예 : 어레이)는 지원하지 않지만 다른 유형은 CDS를 지원할 수 있습니다. 필요한 기능 조합마다 고유 한 인터페이스 유형을 정의하려고하면 악몽이 될 것입니다.
supercat

이제 컬렉션이 할 수있는 모든 작업을 수행 할 수 있지만 모든 트랜잭션을 기록하는 개체로 컬렉션을 래핑하는 기능을 원한다고 상상해보십시오. 몇 개의 포장지가 필요할까요? 모든 컬렉션이 자신의 능력을 식별하는 속성을 포함하는 공통 인터페이스에서 상속 된 경우 하나의 랩퍼로 충분합니다. 그러나 모든 인터페이스가 다르면 수십 개가 필요합니다.
supercat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.