클래스가 하나만 구현할 때 인터페이스를 사용해야합니까?


243

여러 클래스가 일련의 규칙과 구현을 준수하는 인터페이스의 요점이 아닌가?



16
또는 더 쉽게 unittest 할 수 있습니다.

11
여러 클래스가 인터페이스를 구현하고 코드가 인터페이스에 종속되도록하는 것은 단위 테스트를 위해 격리하는 데 필수적입니다. 단위 테스트를 수행하는 경우 해당 인터페이스를 구현하는 다른 클래스가 있습니다.
StuperUser

2
이 질문에 대한 Reddit 토론 .
yannis

6
퍼블릭 필드와 메소드는 그 자체로 "인터페이스"입니다. 다형성의 부족이 의도적으로 계획된 경우 인터페이스를 사용할 이유가 없습니다. 다른 사람들이 언급 한 단위 테스트는 계획된 다형성의 사용입니다.
mike30

답변:


205

엄밀히 말하면, YAGNI 는 적용 되지 않습니다 . 즉, 인터페이스를 작성하는 데 소요되는 시간은 최소화됩니다. 특히 대부분의 작업을 수행하는 편리한 코드 생성 도구가있는 경우에 특히 그렇습니다. 인터페이스가 필요한지 확실하지 않은 경우 인터페이스 정의를 지원하는쪽으로 잘못하는 것이 좋습니다.

또한 단일 클래스에서도 인터페이스를 사용하면 프로덕션이 아닌 단위 테스트를위한 또 다른 모의 구현을 제공 할 수 있습니다. Avner Shahar-Kashtan의 대답 은이 시점에서 확장됩니다.


84
+1 테스트는 거의 항상 어쨌든 두 가지 구현을 의미
JK합니다.

24
@YannisRizos Yagni 때문에 후자의 주장에 동의하지 않습니다. 클래스를 사용하여 CFoo를 IFoo로 대체하는 것처럼 클래스 공용 메소드에서 인터페이스를 크랭크하는 것은 사소한 일입니다. 필요에 따라 미리 작성할 필요는 없습니다.
Dan Neely

5
나는 아직도 당신의 추론을 따르고 있는지 확실하지 않습니다. 코드 생성 도구를 사용하면 사실을 더 저렴하게 추가 할 수 있으므로 명시 적으로 필요하기 전에 인터페이스를 작성해야 할 이유가 훨씬 적습니다.
Dan Neely

6
누락 된 인터페이스는 YAGNI에 대한 좋은 예가 아니라 "깨진 창"및 누락 된 문서에 적합합니다. 클래스의 사용자는 실제로 추상화 대신 구현에 대해 코드를 작성해야합니다.
Fabio Fracassi

10
테스트 프레임 워크를 만족시키기 위해 왜 의미없는 분열로 코드베이스를 오염 시키는가? 진심으로, 나는 WTF를 분류하려고하는 독창적 인 JavaScript 클라이언트 측 사람입니다 .C # 및 Java 개발자 OOD 구현에는 잘못되어 있습니다. t IDE에서 손을 떼고 대학에서 그런 일을 할 때 깨끗하고 읽기 쉬운 코드를 작성하는 방법을 배울 때까지 IDE를 다시 사용하지 못하게하십시오. 그건 외설스러운 일이야
Erik Reppen

144

인터페이스가 필요한지 여부는 인터페이스 를 구현할 클래스 수에 따라 달라 지지 않습니다 . 인터페이스는 응용 프로그램의 여러 하위 시스템 간의 계약 을 정의하기위한 도구입니다 . 실제로 중요한 것은 응용 프로그램을 하위 시스템으로 나누는 방법입니다. 클래스를 구현하는 클래스 수에 상관없이 캡슐화 된 서브 시스템에 대한 프론트 엔드 인터페이스가 있어야합니다.

매우 유용한 경험 법칙은 다음과 같습니다.

  • 클래스가 클래스를 Foo직접 참조 BarImpl하게 Foo할 때마다 변경할 때마다 변경하도록 강요 합니다 BarImpl. 기본적으로 두 클래스로 나뉘어 진 하나의 코드 단위로 처리합니다.
  • 당신이 만드는 경우 Foo참고하여주십시오 인터페이스 Bar , 당신은 대신에 자신을 투입하고 에 대한 변경 사항 Foo을 변경할 때를 BarImpl.

애플리케이션의 주요 지점에서 인터페이스를 정의하는 경우, 지원해야하는 메소드와 그렇지 않아야하는 메소드를 신중하게 고려하고 구현이 어떻게 작동해야하는지 (및 어떻게 수행되지 않아야하는지) 응용 프로그램이 이러한 주석 인터페이스는 일종의 제공하기 때문에 이해하기가 훨씬 더 쉬울 것이다 사양 이 어떻게의 응용 프로그램에 대한 설명의 의도 된 행동을. 이렇게하면 코드를 훨씬 쉽게 읽을 수 있습니다 ( "이 코드의 기능은 무엇입니까?"라고 묻지 않고 "이 코드는 어떻게해야합니까?").

이 모든 것 외에도 실제로 인터페이스는 별도의 컴파일을 촉진합니다. 인터페이스는 컴파일하기가 쉽지 않고 구현보다 종속성이 적기 때문에 인터페이스 Foo를 사용하기 위해 클래스 를 작성 Bar하면 일반적으로 다시 컴파일 BarImpl하지 않고도 다시 컴파일 할 수 있습니다 Foo. 대규모 응용 프로그램에서는 시간을 많이 절약 할 수 있습니다.


14
가능하다면 나는 이것을 한 번 이상 공표 할 것이다. IMO는이 질문에 대한 최고의 답변입니다.
Fabio Fracassi

4
클래스가 하나의 역할 만 수행하는 경우 (즉, 정의 된 인터페이스가 하나 뿐인 경우) 공개 / 개인 메소드를 통해이를 수행하지 않는 이유는 무엇입니까?
Matthew Finlay

4
1. 코드 구성 서명과 문서 주석 만있는 자체 파일에 인터페이스를 사용하면 코드를 깨끗하게 유지할 수 있습니다. 2. 비공개로 유지하려는 메소드를 공개하도록하는 프레임 워크 (예 : 공개 세터를 통해 종속성을 주입하는 컨테이너). 3. 이미 언급 한 바와 같이 별도의 편집; 클래스는 경우 Foo인터페이스에 따라 Bar다음 BarImpl다시 컴파일 할 필요없이 변경 될 수 있습니다 Foo. 4. 공개 / 개인 오퍼보다 세분화 된 액세스 제어 (다른 인터페이스를 통해 동일한 클래스를 두 클라이언트에 노출).
sacundim

3
그리고 마지막으로 : 5. 클라이언트는 (이상적으로) 내 모듈에 몇 개의 클래스가 있는지 또는 얼마나 많은 클래스를 말하거나 말할 수 있는지 상관하지 않아야합니다. 그들이 볼 수있는 것은 일련의 유형 이며, 공을 굴릴 수있는 일부 공장이나 파사드입니다. 즉, 종종 라이브러리가 구성하는 클래스를 캡슐화하는 데 가치가 있다고 생각합니다.
sacundim

4
@sacundim BarImpl을 If you make class Foo refer directly to class BarImpl, you're strongly committing yourself to change Foo every time you change BarImpl변경할 때 Foo에서 피할 수있는 변경 사항은 무엇 interface Bar입니까? BarImpl에서 메소드의 서명 및 기능이 변경되지 않는 한 Foo는 인터페이스 (인터페이스의 전체 목적)가 없어도 변경이 필요하지 않습니다. 나는 하나의 클래스, 즉 BarImplBar를 구현 하는 시나리오에 대해 이야기 하고 있습니다. 멀티 클래스 시나리오의 경우 Dependency Inversion Principle과 인터페이스가 어떻게 도움이되는지 이해합니다.
Shishir Gupta

101

인터페이스는 동작, 즉 함수 / 방법의 프로토 타입 집합을 정의하도록 지정됩니다. 인터페이스를 구현하는 형식은 해당 동작을 구현하므로 이러한 형식을 처리 할 때 해당 동작이 무엇인지 (부분적으로) 알고 있습니다.

인터페이스가 정의한 동작이 한 번만 사용된다는 것을 알고 있으면 인터페이스를 정의 할 필요가 없습니다. 키스 (간단하고 바보처럼 유지)


해당 클래스가 일반 클래스 인 경우 한 클래스에서 인터페이스를 사용할 수있는 것은 아닙니다.
John Isaiah Carmona

또한 "추출 인터페이스"리팩토링을 사용하여 최신 IDE에서 쉽게 필요할 때 인터페이스를 도입 할 수 있습니다.
Hans-Peter Störr

Joshua Bloch et al.과 같은 저자가 완벽하게 수용하고 홍보하는 공개 정적 메소드와 개인 생성자가있는 유틸리티 클래스를 고려하십시오.
Darrell Teague

63

이론적으로 인터페이스를 위해 인터페이스를 가져서는 안되지만 Yannis Rizos의 답변 은 다음과 같은 복잡한 문제를 암시합니다.

단위 테스트를 작성하고 Moq 또는 FakeItEasy와 같은 모의 프레임 워크를 사용하면 (내가 사용한 가장 최근의 두 가지 이름을 지정하기 위해) 인터페이스를 구현하는 다른 클래스를 암시 적으로 만듭니다. 코드 또는 정적 분석을 검색하면 구현이 하나 뿐이지 만 실제로 내부 모의 구현이 있다고 주장 할 수 있습니다. 모의 작성을 시작할 때마다 인터페이스 추출이 의미가 있음을 알게 될 것입니다.

그러나 더 많은 것이 있습니다. 암시 적 인터페이스 구현이있는 시나리오가 더 있습니다. 예를 들어 .NET의 WCF 통신 스택을 사용하면 원격 서비스에 대한 프록시가 생성되어 인터페이스가 다시 구현됩니다.

깨끗한 코드 환경에서 나는 나머지 답변에 동의합니다. 그러나 인터페이스를 사용할 수있는 프레임 워크, 패턴 또는 종속성에주의하십시오.


2
+1 : 인터페이스를 사용하고 인터페이스를 사용하는 프레임 워크 (예 : JMock 등)를 사용하면 실제로 시간을 절약 할 수 있으므로 YAGNI가 적용되는지 확실하지 않습니다.
데코

4
@Deco : 좋은 조롱 프레임 워크 (JMock)는 인터페이스가 필요하지 않습니다.
Michael Borgwardt

12
조롱 프레임 워크의 제한으로 인해 인터페이스를 만드는 것은 나에게 끔찍한 이유처럼 보입니다. 예를 들어, 클래스를 조롱하는 EasyMock을 사용하는 것은 어쨌든 인터페이스만큼 쉽습니다.
Alb

8
나는 그것이 다른 방향이라고 말할 것입니다. 테스트에서 모의 ​​객체를 사용한다는 것은 정의에 따라 인터페이스에 대한 대체 구현을 만드는 것을 의미합니다. 자신 만의 FakeImplementation 클래스를 만들거나 모의 프레임 워크가 큰 도움이 될 수 있습니다. EasyMock과 같은 몇 가지 프레임 워크가있을 수 있으며 다양한 해킹과 저수준 트릭을 사용하여 구체적인 클래스를 조롱하고 더 많은 힘을 얻을 수 있습니다! 그러나 개념 상 모의 객체는 계약에 대한 대체 구현입니다.
Avner Shahar-Kashtan

1
프로덕션 환경에서 테스트를 시작하지 않습니다. 왜 인터페이스가 필요하지 않은 클래스의 모의 인터페이스가 필요한가?
Erik Reppen

32

아니요, 필요하지 않으며 모든 클래스 참조에 대해 자동으로 인터페이스를 만드는 것이 안티 패턴이라고 생각합니다.

모든 것을 위해 Foo / FooImpl을 만드는 데 실제 비용이 있습니다. IDE는 인터페이스 / 구현을 무료로 만들 수 있지만, 코드를 탐색 할 때는 F3 / F12에서 foo.doSomething()인터페이스 구현으로 가고자하는 실제 구현이 아닌 인지적인 추가로드가 있습니다. 또한 모든 것을 위해 하나가 아닌 두 개의 파일이 복잡합니다.

따라서 실제로 무언가를 위해 필요할 때만해야합니다.

이제 반론을 다루고 있습니다.

의존성 주입 프레임 워크에 대한 인터페이스가 필요합니다

프레임 워크를 지원하는 인터페이스는 레거시입니다. Java에서 인터페이스는 CGLIB 이전의 동적 프록시에 대한 요구 사항이었습니다. 오늘날에는 일반적으로 필요하지 않습니다. EJB3, Spring 등에서 더 이상 필요하지 않은 진보와 개발자 생산성의 이점으로 간주됩니다.

단위 테스트를위한 모의가 필요합니다

자신의 모형을 작성하고 실제 구현이 두 개인 경우 인터페이스가 적합합니다. 코드베이스에 FooImpl과 TestFoo가 모두 있으면이 토론이 시작되지 않았을 것입니다.

그러나 Moq, EasyMock 또는 Mockito와 같은 조롱 프레임 워크를 사용하는 경우 클래스를 조롱 할 수 있으며 인터페이스가 필요하지 않습니다. foo.method = mockImplementation메소드를 지정할 수있는 동적 언어 로 설정 하는 것과 유사합니다 .

의존성 역전 원리 (DIP)를 준수하는 인터페이스가 필요합니다

DIP는 구현이 아닌 계약 (인터페이스)에 의존하여 빌드한다고 말합니다. 그러나 클래스는 이미 계약과 추상화입니다. 이것이 공개 / 비공개 키워드의 목적입니다. 대학에서 표준 예제는 매트릭스 또는 다항식 클래스와 같습니다. 소비자는 매트릭스를 생성하고 추가하는 등 공용 API를 가지고 있지만 매트릭스가 희박하거나 밀도가 높은 형태로 구현되는지는 신경 쓰지 않습니다. 그 점을 증명하기 위해 IMatrix 또는 MatrixImpl이 필요하지 않았습니다.

또한 DIP는 주요 모듈 경계뿐만 아니라 모든 클래스 / 메소드 호출 수준에서 과도하게 적용되는 경우가 많습니다. DIP를 과도하게 적용한다는 신호는 인터페이스와 구현이 잠금 단계에서 변경되어 하나의 파일 대신 변경하기 위해 두 개의 파일을 터치해야한다는 것입니다. DIP를 적절하게 적용하면 인터페이스를 자주 변경할 필요가 없습니다. 또한 인터페이스에 하나의 실제 소비자 (자체 응용 프로그램) 만 있음을 나타냅니다. 다양한 앱에서 사용할 클래스 라이브러리를 구축하는 경우 다른 이야기.

이것은 조롱에 대한 Bob Martin 아저씨의 견해입니다. 주요 건축 경계에서만 조롱하면됩니다. 웹앱에서 HTTP 및 DB 액세스는 주요 경계입니다. 사이에있는 모든 클래스 / 메소드 호출은 그렇지 않습니다. DIP도 마찬가지입니다.

또한보십시오:


4
수퍼 클래스 생성자가 실행되는 것을 막을 수있는 방법이 없기 때문에 인터페이스 (최소한 Java 및 C #에서)가 아닌 모의 클래스는 더 이상 사용되지 않는 것으로 간주해야합니다. 생성자 코드에 대해 생각할 필요가 없으므로 인터페이스를 조롱하는 것이 더 안전하고 쉽습니다.
Jules

4
조롱 클래스에 문제가 발생하지 않았지만 IDE 탐색이 예상대로 작동하지 않아 좌절했습니다. 실제 문제를 해결하는 것은 가상의 문제를 능가합니다.
wrschneider

1
@Jules Java로 생성자를 포함하여 구체적 클래스를 조롱 할 수 있습니다.
assylias

1
@assylias 생성자가 실행되는 것을 어떻게 방지합니까?
Jules

2
@Jules 그것은 당신의 조롱 프레임 워크에 달려 있습니다-예를 들어, jmockit을 사용하면 그냥 작성할 수 new Mockup<YourClass>() {}있으며 인터페이스를 비롯하여 추상 클래스 또는 구체적 클래스이든 생성자를 포함하여 전체 클래스가 조롱됩니다. 원하는 경우 생성자 동작을 "재정의"할 수도 있습니다. Mockito 또는 Powermock에 동등한 방법이 있다고 가정합니다.
assylias

22

울타리의 양쪽에 대한 답변을 다음과 같이 요약 할 수 있습니다.

잘 설계하고 인터페이스가 필요한 곳에 인터페이스를 배치하십시오.

Yanni의 답변에 대한 답변 에서 언급했듯이 인터페이스에 대해 단단하고 빠른 규칙을 가질 수 있다고 생각하지 않습니다. 규칙은 정의상 유연해야합니다. 인터페이스에 대한 나의 규칙은 인터페이스를 API를 생성 할 때마다 사용해야한다는 것입니다. 그리고 한 책임 영역에서 다른 영역으로 경계를 넘어가는 곳이라면 어디에서나 API를 만들어야합니다.

(끔찍하게 고안된) 예를 들어 Car수업을 만들고 있다고 가정 해 봅시다 . 수업에는 반드시 UI 레이어가 필요합니다. 이 특정 예에서,는의 형태를 취하고 IginitionSwitch, SteeringWheel, GearShift, GasPedal, 및 BrakePedal. 이 자동차에는가 포함되어 있으므로을 AutomaticTransmission(를) 필요로하지 않습니다 ClutchPedal. (그리고 이것은 끔찍한 차이 기 때문에 A / C, 라디오 또는 좌석이 없습니다. 사실, 마루판도 없습니다. 스티어링 휠에 매달아 서 최선을 다해야합니다!)

이러한 클래스 중 어느 인터페이스가 필요한가요? 대답은 디자인에 따라 전부이거나 아예 없을 수 있습니다.

다음과 같은 인터페이스를 가질 수 있습니다.

Interface ICabin
    Event IgnitionSwitchTurnedOn()
    Event IgnitionSwitchTurnedOff()
    Event BrakePedalPositionChanged(int percent)
    Event GasPedalPositionChanged(int percent)
    Event GearShiftGearChanged(int gearNum)
    Event SteeringWheelTurned(float degree)
End Interface

이 시점에서 해당 클래스의 동작은 ICabin 인터페이스 / API의 일부가됩니다. 이 예제에서 클래스 (일부가있는 경우)는 몇 가지 속성과 함수 또는 두 개로 단순 할 수 있습니다. 그리고 설계에 내재적으로 언급 한 것은이 클래스가 ICabin의 구체적인 구현을 지원하기 위해서만 존재하며 자체적으로 존재할 수 없거나 ICabin 컨텍스트 외부에서 의미가 없다는 것입니다.

개인 멤버를 단위 테스트하지 않는 것과 같은 이유로 공개 API를 지원하기 위해서만 존재 하므로 API를 테스트하여 동작을 테스트해야합니다.

따라서 클래스가 다른 클래스를 지원하기 위해 존재하고 개념적으로 실제로 자신의 도메인이 없는 것으로 보는 경우 인터페이스를 건너 뛰는 것이 좋습니다. 그러나 클래스가 자신의 도메인을 가질만큼 성장했다고 생각할만큼 충분히 중요하다면 인터페이스를 제공하십시오.


편집하다:

자주 (이 답변에 포함) 프로그램을 시작할 때 당신에게 의미가없는 '도메인', '종속성'(종종 '주입'과 결합 됨)과 같은 내용을 읽습니다 (확실하지 않았습니다. 나에게 무엇이든 의미). 도메인의 경우 정확히 다음과 같은 소리를 의미합니다.

지배권이나 권력이 행사되는 지역; 주권 또는 연방 등의 재산. 비 유적으로도 사용됩니다. [WordNet sense 2] [1913 웹스터]

내 예제의 관점에서-를 고려해 봅시다 IgnitionSwitch. 미트 스페이스 차량에서 점화 스위치는 다음을 담당합니다.

  1. 사용자 인증 (식별하지 않음) (올바른 키가 필요함)
  2. 실제로 자동차를 시동 할 수 있도록 시동기에 전류를 공급
  3. 점화 시스템에 전류를 공급하여 계속 작동 가능
  4. 차를 멈추기 위해 전류를 차단합니다.
  5. 당신이 그것을 보는 방법에 따라, 대부분의 최신 자동차에는 변속기가 Park를 벗어난 동안 점화에서 키가 제거되는 것을 막는 스위치가있어 도메인의 일부가 될 수 있습니다. (실제로 시스템을 재고하고 재 설계해야합니다 ...)

이러한 속성은 구성하는 도메인 의를 IgnitionSwitch, 또는 다른 말로, 무엇에 대해 알고하는 책임이있다.

에 대한 IgnitionSwitch책임은 없습니다 GasPedal. 점화 스위치는 모든면에서 가스 페달을 완전히 무시합니다. 그것들은 서로 완전히 독립적으로 작동합니다 (두 자동차가 없으면 차가 상당히 가치가 없지만).

원래 언급했듯이 디자인에 따라 다릅니다. IgnitionSwitch켜짐 ( True)과 꺼짐 ( False)의 두 가지 값을 가진을 설계 할 수 있습니다. 또는 제공된 키와 기타 여러 가지 작업을 인증하도록 설계 할 수 있습니다. 개발자가 모래에 선을 그릴 위치를 결정하는 것은 어려운 일입니다. 솔직히 대부분의 시간은 완전히 상대적입니다. 모래의 라인은 중요하지만 API가있는 곳이므로 인터페이스가 있어야합니다.


"자신의 도메인이 있습니다"라는 의미에 대해 더 자세히 설명해 주시겠습니까?
Lamin Sanneh

1
@LaminSanneh, 정교하게. 도움이 되나요?
Wayne Werner

8

없음 (YAGNI) ,이 인터페이스를 사용하여 다른 클래스에 대한 테스트를 작성하는 것을 계획하고, 테스트가 인터페이스를 조롱의 혜택을하지 않는 한.


8

에서 MSDN :

인터페이스는 응용 프로그램이 특정 기능을 제공하기 위해 관련이없는 많은 개체 유형을 요구하는 상황에 더 적합합니다.

여러 인터페이스를 구현할 수있는 단일 구현을 정의 할 수 있으므로 인터페이스는 기본 클래스보다 융통성이 있습니다.

기본 클래스에서 구현을 상속 할 필요가없는 상황에서는 인터페이스가 더 좋습니다.

인터페이스는 클래스 상속을 사용할 수없는 경우에 유용합니다. 예를 들어 구조체는 클래스에서 상속 할 수 없지만 인터페이스를 구현할 수 있습니다.

일반적으로 단일 클래스의 경우 인터페이스를 구현할 필요는 없지만 프로젝트의 미래를 고려할 때 필요한 클래스 동작을 공식적으로 정의하는 것이 유용 할 수 있습니다.


5

질문에 대답하려면 그 이상이 있습니다.

인터페이스의 중요한 측면 중 하나는 의도입니다.

인터페이스는 "데이터를 포함하지 않지만 동작을 노출시키는 추상 유형"입니다.- 인터페이스 (계산) 따라서 인터페이스가 올바른 패턴보다 클래스가 지원하는 동작 또는 동작 집합 인 경우입니다. 그러나 행동 (들)이 클래스에 의해 구체화 된 개념에 내재되어 있다면, 당신은 전혀 인터페이스를 원하지 않을 것입니다.

물어볼 첫 번째 질문은 당신이 표현하려고하는 것 또는 과정의 본질이 무엇인가입니다. 그런 다음 주어진 방식으로 자연을 구현하는 실질적인 이유를 따르십시오.


5

이 질문을 했으므로 인터페이스가 여러 구현을 숨기는 데 관심이 있다는 것을 이미 알고 있다고 가정합니다. 이것은 의존성 역전 원리에 의해 나타날 수 있습니다.

그러나 인터페이스를 가질 필요가 있는지 여부는 구현 횟수에 의존하지 않습니다. 인터페이스의 실제 역할은 구현 방법 대신 어떤 서비스를 제공해야하는지에 대한 계약을 정의하는 것입니다.

계약이 정의되면 둘 이상의 팀이 독립적으로 작업 할 수 있습니다. 모듈 A에서 작업 중이고 모듈 B에 의존한다고 가정 해보십시오 .B에 인터페이스를 만들면 인터페이스에 의해 모든 세부 사항이 숨겨져 있기 때문에 B의 구현에 대해 걱정하지 않고도 작업을 계속할 수 있습니다. 따라서 분산 프로그래밍이 가능해집니다.

모듈 B의 인터페이스 구현은 하나만 있지만 인터페이스는 여전히 필요합니다.

결론적으로 인터페이스는 사용자로부터 구현 세부 사항을 숨 깁니다. 인터페이스 프로그래밍은 계약을 정의하고 더 많은 모듈 식 소프트웨어를 작성하며 단위 테스트를 촉진하고 개발 속도를 높이기 때문에 더 많은 문서를 작성하는 데 도움이됩니다.


2
모든 클래스는 공용 인터페이스 (공용 메소드)와 개인 인터페이스 (구현 세부 사항)를 가질 수 있습니다. 수업의 공개 인터페이스가 계약이라고 주장 할 수있었습니다. 해당 계약을 위해 추가 요소가 필요하지 않습니다.
Fuhrmanator

4

여기에 모든 대답이 매우 좋습니다. 실제로 대부분의 경우 다른 인터페이스를 구현할 필요 가 없습니다 . 그러나 어쨌든 그것을하고 싶을 수도 있습니다 . 내가하는 경우가 있습니다.

이 클래스는
타사 코드를 연결하는 어댑터 클래스를 사용하여 자주 노출하고 싶지 않은 다른 인터페이스를 구현 합니다.

interface NameChangeListener { // Implemented by a lot of people
    void nameChanged(String name); 
} 

interface NameChangeCount { // Only implemented by my class
    int getCount();
}

class NameChangeCounter implements NameChangeListener, NameChangeCount {
    ...
}

class SomeUserInterface {
    private NameChangeCount currentCount; // Will never know that you can change the counter
}

이 클래스는
대부분의 외부 라이브러리와 상호 작용할 때 유출되지 않아야하는 특정 기술을 사용합니다 . 구현이 하나만 있더라도 외부 라이브러리와의 불필요한 연결을 도입하지 않도록 인터페이스를 사용합니다.

interface SomeRepository { // Guarantee that the external library details won't leak trough
    ...
}

class OracleSomeRepository implements SomeRepository { 
    ... // Oracle prefix allow us to quickly know what is going on in this class
}

계층 간 통신
하나의 UI 클래스 만 도메인 클래스 중 하나를 구현하더라도 해당 계층간에 더 나은 분리가 가능하며 가장 중요한 것은 순환 종속성을 피하는 것입니다.

package project.domain;

interface UserRequestSource {
    public UserRequest getLastRequest();
}

class UserBehaviorAnalyser {
    private UserRequestSource requestSource;
}

package project.ui;

class OrderCompleteDialog extends SomeUIClass implements project.domain.UserRequestSource {
    // UI concern, no need for my domain object to know about this method.
    public void displayLabelInErrorMode(); 

    // They most certainly need to know about *that* though
    public UserRequest getLastRequest();
}

대부분의 객체에 메소드의 하위 세트 만 사용할 수 있어야합니다
. 구체적인 클래스에 구성 메소드가있을 때 주로 발생합니다.

interface Sender {
    void sendMessage(Message message)
}

class PacketSender implements Sender {
    void sendMessage(Message message);
    void setPacketSize(int sizeInByte);
}

class Throttler { // This class need to have full access to the object
    private PacketSender sender;

    public useLowNetworkUsageMode() {
        sender.setPacketSize(LOW_PACKET_SIZE);
        sender.sendMessage(new NotifyLowNetworkUsageMessage());

        ... // Other details
    }
}

class MailOrder { // Not this one though
    private Sender sender;
}

그래서 결국 개인 필드를 사용하는 것과 같은 이유로 인터페이스를 사용합니다. 다른 객체는 액세스해서는 안되는 물건에 액세스 할 수 없어야합니다. 나는 그런 경우가있는 경우에만 경우에도, 나는 인터페이스를 도입 클래스를 구현합니다.


2

인터페이스는 정말 중요하지만 가지고있는 인터페이스의 수를 제어하십시오.

거의 모든 것에 대한 인터페이스를 만드는 길을 갔다가 '잘게 썬 스파게티'코드로 끝나기가 쉽습니다. 나는이 주제에 대해 매우 현명한 말을 게시 한 Ayende Rahien의 더 큰 지혜를 미루고있다.

http://ayende.com/blog/153889/limit-your-abstractions-analyzing-a-ddd-application

이것은 전체 시리즈의 첫 번째 게시물이므로 계속 읽으십시오!


'잘게 썬 스파게티'코드는 라비올리 코드 c2.com/cgi/wiki
CurtainDog

라자냐 코드 또는 바클 라바 코드와 비슷하게 들립니다. 레이어가 너무 많습니다. ;-)
dodgy_coder

2

이 경우 여전히 인터페이스를 도입하고 싶은 한 가지 이유는 Dependency Inversion Principle 을 따르는 것 입니다. 즉, 클래스를 사용하는 모듈은 구체적인 구현에 의존하지 않고 추상화 (즉, 인터페이스)에 의존합니다. 상위 레벨 구성 요소를 하위 레벨 구성 요소에서 분리합니다.


2

아무것도 할 이유가 없습니다. 인터페이스는 출력 프로그램이 아닌 사용자를 돕기위한 것입니다. 따라서 인터페이스가 백만 개의 클래스로 구현 되더라도이를 작성해야한다는 규칙은 없습니다. 귀하 또는 귀하의 코드를 사용하는 다른 사람이 모든 구현에 해당하는 것을 변경하려고 할 때 하나를 작성하십시오. 인터페이스를 작성하면 나중에 구현할 다른 클래스를 작성하려는 모든 경우에 도움이됩니다.


1

항상 클래스에 대한 인터페이스를 정의 할 필요는 없습니다.

가치 객체와 같은 간단한 객체에는 여러 구현이 없습니다. 그들은 조롱 할 필요도 없습니다. 구현은 자체적으로 테스트 할 수 있으며 다른 클래스가 테스트 될 때 실제 값 개체를 사용할 수 있습니다.

인터페이스를 만드는 데 비용이 듭니다. 구현과 함께 업데이트해야하며 추가 파일이 필요하며 일부 IDE는 인터페이스가 아니라 구현을 확대 / 축소하는 데 문제가 있습니다.

따라서 구현에서 추상화하려는 고급 클래스에만 인터페이스를 정의합니다.

클래스를 사용하면 인터페이스가 무료로 제공됩니다. 구현 외에도 클래스는 공용 메소드 세트에서 인터페이스를 정의합니다. 이 인터페이스는 모든 파생 클래스에 의해 구현됩니다. 인터페이스를 엄격하게 말하지는 않지만 정확히 같은 방식으로 사용할 수 있습니다. 따라서 클래스 이름 아래에 이미 존재하는 인터페이스를 다시 만들 필요가 없다고 생각합니다.

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