54

나는 숫자를 가지고있다.Processor매우 다른 두 가지 일을하지만 공통 코드에서 호출되는 클래스 ( "제어 반전"상황).

나는 당신이 모든 것을 상속해야하는지 결정할 때 어떤 디자인 고려 사항을 내가인지했는지 (또는 당신이 알고있는, 당신이 USS를 위해) 있어야하는지 궁금합니다.BaseProcessor, 또는 구현IProcessor인터페이스로.


7 답변


143

일반적으로 규칙은 다음과 같이 바뀝니다.

  • 상속은~이다관계.
  • 인터페이스를 구현하면할수있다관계.

이를 좀 더 구체적으로 설명하기 위해 예제를 살펴 보겠습니다. 그만큼System.Drawing.Bitmap수업이다이미지 (그리고 그 자체로, 그것은Image클래스)뿐만 아니라할수있다처리하기 때문에IDisposable인터페이스. 또한할수있다직렬화, 그래서 그것은에서 구현ISerializable인터페이스.

그러나 실제로는 C #에서 다중 상속을 시뮬레이션하는 데 종종 인터페이스가 사용됩니다. 귀하의Processor클래스는 다음과 같은 것을 상속해야합니다.System.ComponentModel.Component, 당신은 선택의 여지가 있지만IProcessor인터페이스.

사실 인터페이스와 추상 기본 클래스는 특정 클래스가 수행 할 수있는 작업을 지정하는 계약을 제공합니다. 이 계약을 선언하기 위해 인터페이스가 필요하다는 것은 일반적인 신화이지만 올바른 것은 아닙니다. 내 생각에 가장 큰 이점은 추상 기본 클래스를 사용하면 하위 클래스에 기본 기능을 제공 할 수 있다는 것입니다. 그러나 기본 기능이 없다면 메서드 자체를 다음과 같이 표시하지 않아도됩니다.abstract파생 된 클래스는 인터페이스를 구현하는 것처럼 스스로 구현해야합니다.

이와 같은 질문에 대한 답변을 보려면 종종.NET Framework 디자인 지침이 클래스와 인터페이스 사이의 선택에 대해 말하고있다 :

일반적으로 클래스는 추상화를 노출하기 위해 선호되는 구조입니다.

인터페이스의 주요 단점은 API의 진화를 허용 할 때 클래스보다 훨씬 융통성이 부족하다는 것입니다. 인터페이스를 출시하면 멤버 세트가 영원히 고정됩니다. 인터페이스를 추가하면 인터페이스를 구현하는 기존 유형이 손상됩니다.

클래스는 훨씬 더 많은 유연성을 제공합니다. 이미 배송 된 수업에 회원을 추가 할 수 있습니다. 메소드가 추상적이 아닌 한 (즉, 메소드의 기본 구현을 제공하는 한) 기존 파생 클래스는 변경되지 않고 계속 작동합니다.

[. . . ]

인터페이스를 선호하는 가장 일반적인 주장 중 하나는 구현과 계약을 분리 할 수 있다는 것입니다. 그러나 인수는 클래스를 사용하여 구현과 계약을 분리 할 수 없다고 잘못 가정합니다. 구체적인 구현과는 별도의 어셈블리에있는 추상 클래스는 이러한 분리를 달성하는 좋은 방법입니다.

일반적인 권장 사항은 다음과 같습니다.

  • 해야 할 것인터페이스를 통한 클래스 정의.
  • 해야 할 것인터페이스 대신 추상 클래스를 사용하여 계약을 구현과 분리합니다. 추상 클래스는 올바르게 정의 된 경우 계약과 구현간에 동일한 수준의 분리를 허용합니다.
  • 해야 할 것값 유형의 다형성 계층 구조를 제공해야하는 경우 인터페이스를 정의하십시오.
  • 중히 여기다다중 상속과 비슷한 효과를 내기 위해 인터페이스를 정의합니다.

크리스 앤더슨 (Chris Anderson)은이 마지막 교리와 특별한 협약을 맺어 다음과 같이 주장했다.

추상 유형은 훨씬 더 나은 버전을 제공하고 향후 확장 성을 허용하지만, 유일한 기본 유형을 구울 수도 있습니다. 인터페이스는 실제로 시간이 지남에 따라 변하지 않는 두 객체 사이의 계약을 정의 할 때 적합합니다. 추상 기본 유형은 유형 패밀리에 대한 공통 기본을 정의하는 데 적합합니다.


  • 고마워요. 훌륭한 답변입니다. 하나의주의 사항 만 있으면 :출판하다"공개 타입"으로서의베이스 - 클래스 (base-class) 그 클래스의 사용자들에게 그곳으로부터 상속받을 것을 권하고 ... (미래에) 공통적 인 행동을 할 수있는 어딘가에 .... 그리고 링크에 감사드립니다. 나는 좋은 독서를해야한다 ;-) - corlettk
  • 인터페이스는 반드시해야 할 일임에 유의해야합니다. - awiebe
  • 이 답변은 Java 8에서 최근 수행 된 변경 사항에 비추어 인터페이스에 기본 메소드를 도입하여보다 사실이됩니다. 다른 방법으로 인터페이스를 구현하는 모든 코드를 깨지 않고 새로운 계약 / 메소드를 도입 할 수있는 다른 방법은 없었습니다. (추상 클래스와 인터페이스 사이의 라인이이 변경으로 인해 더 흐려 지지만) - Harshdeep
  • '헤드 퍼스트 디자인 패턴'의 추진과는 정반대로이 충고를하지 못했습니다. 나는 어떤 방법으로도 전문가가 아니지만 그들의 진언은 상속에 대한 구성보다 선호한다. 누구나 이것을 풀 수 있습니까? - alimack
  • @ali 나는 모순을 전혀 보지 못합니다. 필자가이 답변을 작성한 이후로 오랜 세월이 지났지 만 다시 한 번 감추는 과정에서 나는 구성 상속에 찬성하여지지하는 곳을 볼 수 없습니다. 말할 수 없다.아무것도사실, 작곡에 관한거야. 상속으로 모델링 한 강력한 관계를 필요로하지 않는다면 구성이 더 나은 선택입니다. 필요한 것보다 더 강한 관계를 표현해서는 안됩니다. - Cody Gray

7

리차드,

왜 그들 사이를 선택 하는가? IProcessor 인터페이스가출판 된유형 (시스템의 다른 곳에서 사용하기 위해); IProcessor의 다양한 CURRENT 구현에 공통적 인 동작이있는 경우에는 추상 BaseProcessor 클래스가 해당 공통적 인 동작을 구현하는 실제 좋은 장소가됩니다.

이런 방식으로, 미래에 BaseProcessor의 서비스를위한 것이 아닌 IProcessor가 필요하다면, 그것을 가지고 있지 않을 수도 있고 가능하게 숨길 수도 있습니다 ...하지만 그것을 원한다면 ... 줄일 수 있습니다. 중복 코드 / 개념.

내 겸손한 견해.

건배. 키이스.


  • Keith에게 감사드립니다. 좋은 지적입니다. 다른 것을 선택하면서 나를 물지 않기 위해 다시 올 수있는 고려 사항이 있다면 나는 정말로 방황하고있었습니다. 두 가지 모두 구현하는 것이 좋습니다. - probably at the beach
  • 흠, 나는 이것에 동의하지 않는다. 일반적으로 인터페이스를 구체적으로 구현하지 않고 인터페이스를 공개해서는 안됩니다. 인터페이스 노출과 관련된 또 다른 문제점은 버전 관리입니다 (자세한 내용은 내 대답 참조). 둘 다해야한다고 생각하지 않습니다. 이것이 디자인 결정에 항상 최선의 해답이되는 것은 아닙니다. 두 가지를 모두 시도하면 복잡성이 커지면서 가격이 상승합니다. 실제로 여기서 어떻게 유리한지를보기 위해 노력합니다. 인터페이스가 아닌 추상 기본 클래스를 사용하여 누락 된 정보는 없습니다. - Cody Gray

5

인터페이스는 "계약"이며, 이는 몇 가지 클래스가 원하는 멤버 집합 (속성, 메서드 및 이벤트)을 구현하도록합니다.

기본 클래스 (구체적 또는 추상, 중요하지 않음)는 일부 엔티티의 원형입니다. 이것이 실제 물리적 또는 개념적 공통점을 나타내는 개체입니다.

인터페이스를 언제 사용합니까?

어떤 유형은 선언 할 필요가있을 때마다 적어도 소비자가 관심을 가져야 할 행동과 속성을 가지고 있으며 어떤 작업을 수행하는 데 사용합니다.

기본 클래스 (구체적 및 / 또는 추상)를 사용하는 경우

한 그룹의 엔티티가 같은 아키 타입을 공유 할 때마다 B는 A가 상속 받음을 의미하지만 B는 A가 다르기 때문에 A를 상속 받지만 B는 A로 식별 될 수 있습니다.


예 :

테이블에 대해 이야기 해 봅시다.

  • 우리는 재활용 가능한 테이블을 허용합니다.=>이것은 "IRecyclableTable"과 같은 인터페이스로 정의되어야합니다. 모든 재활용 가능 테이블은 "Recycle"메소드를 갖습니다..

  • 우리는 데스크탑 테이블을 원한다.=>이것은 상속으로 정의해야합니다. "데스크탑 테이블"은 "테이블"입니다. 모든 테이블은 공통적 인 속성 및 동작을 가지며 데스크탑은 데스크탑 테이블이 다른 유형의 테이블과 다르게 작동하도록 동일한 속성 및 동작을 추가합니다.

나는 연관성에 대해 이야기 할 수 있습니다. 객체 그래프에서 두 경우의 의미를 이야기 할 수는 있지만, 제 생각에 개념적 관점에서 논증 할 필요가 있다면, 저는이 논증으로 정확히 대답 할 것입니다.


  • & quot; 재활용 가능한 표 & quot; 하지만, 아마IRecyclable인터페이스는 여러 개의무관계의객체는 재활용 할 수 있어야합니다 (즉, 테이블을 나타내지 않는 다른 클래스). 그리고 나서 나는 여전히 모든 테이블을Table수업 포함DesktopTablePicnicTableFoldingTable. - Cody Gray
  • 그리고 내 텍스트를 다시 확인하면, 나는 당신과 다른 것을 말하고있는 것입니까? :) & quot; 재활용 가능한 표 & quot;에 대한 것을 시작하는 방법을 읽으십시오 : & quot; 예제 & quot;. 단지 객체 그래프, 디자인 또는 실제 사례와 절대적으로 분리 된 예입니다. - Matías Fidemraizer
  • 어쨌든, 제안 해 주셔서 감사 드리며 내 말에 틀리지 마십시오. 안녕하세요. :) - Matías Fidemraizer

1

필자는 디자인 선택에별로 좋지 않지만, 확장 된 멤버 만 있다면 iProcessor 인터페이스를 구현하는 것을 선호 할 것입니다. 확장 할 필요가없는 다른 함수가 있으면 기본 프로세서에서 상속하는 것이 더 좋습니다.


  • @jitendra - 왜이게 더 나은 옵션인지에 대해 좀 더 자세히 설명해 주시겠습니까? - probably at the beach
  • 왜 인터페이스를 선호합니까? 추상 기본 클래스에 비해 인터페이스의 장점은 무엇입니까? 나는 본능에 완전히 동의하지 않는다.항상다중 상속을 구체적으로 시뮬레이트 할 필요가 없다면 인터페이스를 통해 추상 기본 클래스를 선택하십시오. - Cody Gray
  • 몇 가지 함수 만있는 경우 기본 클래스에서 사용할 수있는 모든 함수를 구현할 필요가 없으므로 상속하는 것이 더 적합합니다. 기본 클래스의 전체 함수 및 매개 변수 목록에 대한 충분한 정보가 필요하지 않습니다. 그래서,이 경우, 상속하는 것이 더 합리적입니다. - jitendragarg
  • 또한 기본 클래스가 추후에 사용되기로되어 있더라도 추상이 아닌 일반 기본 클래스를 생성하고 필요한 함수 만 상속 할 수 있습니다. 하지만 인터페이스가 클래스보다 가볍기 때문에이 시나리오에서는 성능이 저하 될 것입니다. 게다가 추상 클래스를 통한 인터페이스를 사용하는 경우 다른 개발자는 기본 클래스의 몇 가지 함수 만 사용하기 위해 모든 함수를 구현할 필요조차 없습니다. 따라서 기본 클래스의 재사용 가능성도 중요한 요소이므로 이러한 선택을하기 전에 고려해야합니다. - jitendragarg
  • @cody gray 내가 잘못하지 않는다면 재사용 성의 경우에 나쁜 생각 인 전체 추상 클래스를 구현한다고 가정한다. 다시 말하지만, 그건 내 관점입니다. 잘못되었을 수도 있습니다. 친절하게도 댓글을 편집하십시오. - jitendragarg

1

디자인의 순결을 제쳐두고, 한 번만 상속 할 수 있습니다. 일부 프레임 워크 클래스를 상속해야하는 경우 질문은 논리적이지 않습니다.

선택의 여지가 있다면 실용적인 방법으로 가장 많은 타이핑을 저장하는 옵션을 선택할 수 있습니다. 어쨌든 평범한 선택입니다.

편집하다:

기본 클래스에 구현이있는 경우 유용 할 수 있습니다. 순수하게 abstact이면 인터페이스 일 수도 있습니다.


  • 이것은 좋은 지적입니다. 종종 기본 클래스를 선택하면 대부분의 입력을 절약 할 수 있습니다. 모든 과부하가 핵심 함수를 호출하는 메소드가있는 경우 기본 클래스에 모든 매개 변수 유효성 검증, 경계 점검 및 함수 오버로드를 배치 할 수 있습니다. 이는 파생 된 클래스가 코어 "고기 및 감자"를 구현하기 만하면된다는 것을 의미합니다. 기능. 인터페이스가있는 옵션이 아닙니다.마다인터페이스를 구현하는 클래스는 매번 모든 작업을 수행해야합니다. - Cody Gray
  • @Cody Gray 마지막 부분에 대해,하지만 기본 클래스가 인터페이스를 구현하고 있으며 인터페이스 구현이 가상이므로 혼합 된 경우입니다! 농담해라, 신경 쓰지 마라. - Matías Fidemraizer

1

선택한 항목이 상관 없으면 항상 인터페이스를 선택하십시오. 더 많은 유연성을 허용합니다. 나중에 변경되지 않도록 보호 할 수 있습니다 (기본 클래스에서 변경해야 할 경우 상속 된 클래스가 영향을받을 수 있음). 또한 세부 정보를보다 잘 캡슐화 할 수 있습니다. Dependency Injection Control의 Inversion of Control을 사용하고 있다면 인터페이스를 선호하는 경향이 있습니다.

또는 상속을 피할 수없는 경우 두 가지를 함께 사용하는 것이 좋습니다. 인터페이스를 구현하는 추상 기본 클래스를 만듭니다. 귀하의 경우, ProcessorBase는 IProcessor를 구현합니다.

ASP.NET MVC의 ControllerBase 및 IController와 유사합니다.


  • 인터페이스가보다 유연하고 미래의 변화가있을 경우이를 정당화한다고 주장하는 것이 흥미 롭습니다. 앞으로 인터페이스에 일부 기능을 추가해야하는 경우에는 어떻습니까? 그 인터페이스를 구현 한 각 클래스는또한방금 추가 한 메소드를 구현해야한다. 기본 클래스는 기본 구현을 제공하기 만하면되므로 기본 클래스의 경우는 그렇지 않습니다. - Cody Gray
  • 향후 변경 사항은 & 다른 이야기가 있습니다. 귀하의 경우에는 더 나은 방법은 추상적 인 클래스를 사용하는 것입니다,하지만 그렇게 할 때, 나는 또한 인터페이스 된 추상 클래스 (I-와 -베이스 모두)를 선호합니다. 또한 다른 관점에서 볼 때 디자인을 사용하는 것이 아닌 & # 39; 클래스를 사용하는 사용자라면 여러 가지 방법보다는 두 가지 또는 세 가지 방법을 사용하는 것이 더 바람직합니다. 이 경우, 인터페이스는 관심사의 분리에서 더 낫다. 하나의 인터페이스를 사용하여 다른 작업을 수행 할 수 있습니다. 그리고 모든 인터페이스는 사실 하나의 단일 클래스 일 수 있습니다. - Hendry Ten
  • 인터페이스에 기능 추가 : 일반적으로 인터페이스에 새로운 기능을 추가하지는 않습니다 (계약 유형이나 저장소의 경우는 제외). 새 인터페이스를 만들 수 있습니다 (C #의 인터페이스는 다른 인터페이스에서 파생 될 수 있음). 관심사가 잘 구현됩니다. - Hendry Ten
  • 이런 종류의 주장을 좋아하지 않습니다. 리팩토링 이유로 인해 디자인을 수행하지 않아야합니다. 좋은 디자인은 리팩터링하기 쉽고 인터페이스 사용 상속과 관련이 없습니다. 소프트웨어는 확실히 바뀌지 만, 어떤 종류의 요구 사항이 초과 근무를 해결할 것인가에주의를 기울여서 할 수 있습니다. 따라서 상속을 사용해도 잘못하지 않으면 좋은 상속 트리가 논리를 추가, 수정 및 / 또는 제거합니다 그 의미는 "있는 그대로"유지되어야한다. - Matías Fidemraizer

-1

SOLID 원칙은 프로젝트의 유지 보수성과 확장 성을 제공하므로 상속을 통한 인터페이스를 선호합니다.

또한 인터페이스에 "추가 기능"을 추가해야하는 경우 가장 좋은 옵션은 인터페이스 분할 기준 인 SOLID에 따라 새 인터페이스를 모두 만드는 것입니다.

연결된 질문


최근 질문