추상 클래스의 인터페이스


30

동료와 기본 클래스와 인터페이스의 관계에 대한 의견이 다릅니다. 인터페이스 구현이 필요할 때 해당 클래스를 사용할 수 없다면 클래스가 인터페이스를 구현해서는 안된다고 생각합니다. 즉, 다음과 같은 코드를보고 싶습니다.

interface IFooWorker { void Work(); }

abstract class BaseWorker {
    ... base class behaviors ...
    public abstract void Work() { }
    protected string CleanData(string data) { ... }
}

class DbWorker : BaseWorker, IFooWorker {
    public void Work() {
        Repository.AddCleanData(base.CleanData(UI.GetDirtyData()));
    }
}

DbWorker는 IFooWorker 인터페이스를 얻는 것인데, 인터페이스의 인스턴스화 가능한 구현이기 때문입니다. 계약을 완전히 이행합니다. 내 동료는 거의 동일한 것을 선호합니다.

interface IFooWorker { void Work(); }

abstract class BaseWorker : IFooWorker {
    ... base class behaviors ...
    public abstract void Work() { }
    protected string CleanData(string data) { ... }
}

class DbWorker : BaseWorker {
    public void Work() {
        Repository.AddCleanData(base.CleanData(UI.GetDirtyData()));
    }
}

기본 클래스가 인터페이스를 얻는 곳에서 이로 인해 기본 클래스의 모든 상속자도 해당 인터페이스에 있습니다. 이것은 나에게 버그가 있지만 "기본 클래스 외부에서 인터페이스 구현으로 독자적으로 설 수는 없습니다"라는 구체적인 이유를 생각해 낼 수 없습니다.

그의 방법 대 나의 방법의 장단점은 무엇이며 왜 하나를 다른 것보다 사용해야합니까?


당신의 제안은 다이아몬드 상속 과 매우 유사하며 , 이는 더 많은 혼란을 야기 할 수 있습니다.
Spoike

@Spoike : 어떻게 보는가?
Niing

@ 주석에 내가 제공 한 링크에는 간단한 클래스 다이어그램과 그에 대한 간단한 한 줄 설명이 있습니다. 상속 구조는 기본적으로 다이아몬드 모양 (즉, ♦ ️)으로 그려지기 때문에 "다이아몬드"문제라고합니다.
Spoike

@Spoike : 왜이 ​​질문이 다이아몬드 상속을 야기할까요?
Niing

1
@Niing : 동일한 메소드를 사용하여 BaseWorker와 IFooWorker를 Work상속하는 것은 다이아몬드 상속 문제입니다 (이 경우 둘 다 Work메소드에 대한 계약을 이행 함 ). Java에서는 인터페이스의 메소드를 구현 Work해야하므로 프로그램이 사용해야 하는 메소드의 문제를 피할 수 있습니다. 그러나 C ++과 같은 언어는이를 명확하게하지 않습니다.
Spoike

답변:


24

인터페이스 구현이 필요할 때 해당 클래스를 사용할 수 없다면 클래스가 인터페이스를 구현해서는 안된다고 생각합니다.

BaseWorker그 요구 사항을 충족시킵니다. BaseWorker객체를 직접 인스턴스화 할 수 없다고 해서 계약을 이행 하는 BaseWorker 포인터 를 가질 수 없다는 의미는 아닙니다 . 사실, 그것은 추상 클래스의 요점입니다.

또한 게시 한 단순화 된 예제에서 말하기는 어렵지만 문제의 일부는 인터페이스와 추상 클래스가 중복되어있을 수 있습니다. 당신이 구현하는 다른 클래스가 없다면 IFooWorker않아도 되지 에서 파생를 BaseWorker, 당신은 모두의 인터페이스가 필요하지 않습니다. 인터페이스는 다중 상속을 쉽게하는 몇 가지 추가 제한 사항이있는 추상 클래스입니다.

단순화 된 예에서, 보호 된 메소드의 사용, 파생 클래스의 기본을 명시 적으로 언급하고 인터페이스 구현을 선언 할 명확한 장소가 부족하다는 것은 다시 말하지만 상속 대신 부적절한 상속을 사용하고 있다는 경고 신호입니다. 구성. 그 상속이 없다면, 당신의 모든 질문은 혼란스러워집니다.


1
+1은 BaseWorker직접 인스턴스화 할 수 없기 때문에 주어진 BaseWorker myWorker것이 구현 되지 않는다는 것을 의미 하지는 않습니다 IFooWorker.
Avner Shahar-Kashtan

2
인터페이스가 추상 클래스라는 것에 동의하지 않습니다. 추상 클래스는 일반적으로 일부 구현 세부 사항을 정의 합니다 (그렇지 않으면 인터페이스가 사용되었을 것임). 이러한 세부 사항이 마음에 들지 않으면 이제 기본 클래스의 모든 사용법을 다른 것으로 변경해야합니다. 인터페이스가 얕습니다. 종속 항목과 종속 항목 간의 "플러그 앤 소켓"관계를 정의합니다. 따라서, 구현 세부 사항 에 대한 긴밀한 결합 은 더 이상 문제가되지 않습니다. 인터페이스를 상속받는 클래스가 원하는 것이 아닌 경우, 그것을 제거하고 완전히 다른 것을 넣습니다. 코드가 덜 신경 쓰일 수 있습니다.
KeithS

5
둘 다 장점이 있지만 기본 구현을 정의하는 추상 클래스가 있는지 여부에 관계없이 인터페이스는 항상 종속성에 사용해야합니다 . 인터페이스와 추상 클래스의 조합은 두 세계의 최고를 제공합니다. 인터페이스는 얕은 표면 플러그 앤 소켓 관계를 유지하면서 추상 클래스는 유용한 공통 코드를 제공합니다. 인터페이스 아래의 레벨을 마음대로 제거하고 교체 할 수 있습니다.
KeithS

하여 "포인터" 당신은 의미합니까 객체 인스턴스 (포인터가 참조 할 것)? 인터페이스는 클래스가 아니며 유형이며 대체가 아닌 다중 상속의 불완전한 대체 역할을 할 수 있습니다. 즉, "클래스에 여러 소스의 동작 포함".
samis

46

당신의 동료와 동의해야합니다.

두 예제 모두에서 BaseWorker는 추상 메소드 Work ()를 정의합니다. 이는 모든 서브 클래스가 IFooWorker의 계약을 충족시킬 수 있음을 의미합니다. 이 경우 BaseWorker가 인터페이스를 구현해야하며 해당 구현은 해당 서브 클래스에 의해 상속 될 것이라고 생각합니다. 이렇게하면 각 서브 클래스가 실제로 IFooWorker (DRY 원칙)임을 명시 적으로 표시하지 않아도됩니다.

Work ()를 BaseWorker의 메소드로 정의하지 않았거나 IFooWorker에 BaseWorker의 서브 클래스가 원하지 않거나 필요하지 않은 다른 메소드가있는 경우 (분명히) 어떤 서브 클래스가 실제로 IFooWorker를 구현하는지 표시해야합니다.


11
+1도 같은 내용을 게시했을 것입니다. 미안하지만,이 경우에도 동료가 옳다고 생각합니다. 계약 이행을 보장하는 것이면 인터페이스, 추상 클래스 또는 구체적인 클래스에 의해 보증이 이행되는지 여부에 관계없이 계약을 상속해야합니다. 간단히 말해 : 보증인은 보증 계약을 상속해야합니다.
Jimmy Hoffa

2
나는 일반적으로 동의하지만 "base", "standard", "default", "generic"등과 같은 단어는 클래스 이름에서 코드 냄새라는 것을 지적하고 싶습니다. 추상 기본 클래스의 이름이 인터페이스와 거의 동일하지만 족제비 단어 중 하나가 포함 된 경우 종종 잘못된 추상화의 징조입니다. 인터페이스는 뭔가을 정의 하지 가 무엇인지, 유형 상속하는지 정의 입니다 . 당신이있는 경우 IFoo와를 BaseFoo다음 중 하나가 있음을 의미한다 IFoo적절하게 세분화 된 인터페이스가 아니거나 그 BaseFoo조성이 더 나은 선택이 될 수있는 곳 상속을 사용하고 있습니다.
Aaronaught

@Aaronaught 좋은 지적이지만, IFoo의 모든 또는 대부분의 구현이 상속해야 할 것이있을 수도 있지만 (서비스 핸들 또는 DAO 구현과 같은) 사실이있을 수 있습니다.
Matthew Flynn

2
그런 다음 기본 클래스를 호출 할 수 없습니다 MyDaoFoo또는 SqlFoo또는 HibernateFoo또는 무엇이든, 그것은 구현하는 클래스의 하나의 가능한 나무의 것을 나타 내기 위해서 IFoo? 기본 클래스가 특정 라이브러리 또는 플랫폼에 연결되어 있고 이름에 언급이 없다면 더
냄새가납니다

@Aaronaught-그렇습니다. 나는 완전히 동의합니다.
Matthew Flynn

11

나는 일반적으로 당신의 동료에 동의합니다.

기본 클래스가 IFooWorker 메서드의 노출을 강제하더라도 인터페이스는 자식 클래스로만 구현됩니다. 우선, 중복입니다. 자식 클래스가 인터페이스를 구현하는지 여부에 관계없이 노출 된 BaseWorker 메서드를 재정의해야하며 IFooWorker 구현은 BaseWorker의 도움을 받든 관계없이 기능을 노출해야합니다.

이것은 또한 계층을 모호하게 만듭니다. "모든 IFooWorkers는 BaseWorkers입니다"는 반드시 실제 진술 일 필요는 없으며 "All BaseWorkers는 IFooWorkers"도 아닙니다. 따라서 IFooWorker 또는 BaseWorker를 구현할 수있는 인스턴스 변수, 매개 변수 또는 반환 유형을 정의하려는 경우 (처음에 상속해야하는 이유 중 하나 인 공통 노출 기능을 활용) 이것들은 모든 것을 포괄하는 것으로 보장됩니다. 일부 BaseWorkers는 IFooWorker 유형의 변수에 지정할 수 없으며 그 반대도 가능합니다.

동료의 모델은 사용 및 교체가 훨씬 쉽습니다. "모든 BaseWorkers는 IFooWorkers입니다"는 이제 진정한 진술입니다. IFooWorker 종속성에 BaseWorker 인스턴스를 문제없이 제공 할 수 있습니다. 반대말 "모든 IFooWorkers는 BaseWorkers입니다"는 사실이 아닙니다. BaseWorker를 BetterBaseWorker로 바꿀 수 있으며 소비 코드 (IFooWorker에 따라 다름)는 차이를 말할 필요가 없습니다.


나는이 대답의 소리를 좋아하지만 마지막 부분을 따르지 않습니다. BetterBaseWorker가 IFooWorker를 구현하는 독립적 인 추상 클래스가 될 것을 제안합니다. 따라서 가상 플러그인은 간단히 "오, 소년! Better Base Worker가 나왔습니다!" BaseBaseer 참조를 BetterBaseWorker로 변경하고 하루에 호출하십시오 (중복 코드의 거대한 덩어리를 제거 할 수있는 멋진 새로운 반환 값으로 재생할 수 있습니까)?
Anthony

더 많거나 적습니다. 가장 큰 장점은 대신 BaseBaseer에 대한 참조 수를 줄여서 BetterBaseWorkers로 변경해야한다는 것입니다. 대부분의 코드는 IFooWorker를 사용하는 대신 구체적인 클래스를 직접 참조하지 않으므로 IFooWorker 종속성 (클래스의 속성, 생성자 또는 메서드의 속성)에 할당 된 내용을 변경할 때 IFooWorker를 사용하는 코드에는 차이가 없어야합니다. 사용법에서.
KeithS

6

이 답변에 경고를 추가해야합니다. 기본 클래스를 인터페이스에 연결하면 해당 클래스의 구조에 힘이 생깁니다. 기본 예제에서 두 가지를 결합해야한다는 것은 결코 쉬운 일이 아니지만 모든 경우에 해당되는 것은 아닙니다.

Java의 컬렉션 프레임 워크 클래스를 사용하십시오.

abstract class AbstractList
class LinkedList extends AbstractList implements Queue

계약이 LinkedList에 의해 구현 되었다는 사실은 AbstractList에 관심을 가지지 않았습니다 .

두 경우의 차이점은 무엇입니까? BaseWorker 의 목적은 IWorker 에서 작업을 구현하기 위해 항상 (이름과 인터페이스로 전달 된) 입니다. AbstractListQueue 의 목적 은 다양하지만 전자의 후손은 여전히 ​​후자의 계약을 구현할 수 있습니다.


이것은 반 시간에 일어난다. 그는 항상 기본 클래스에서 인터페이스를 구현하는 것을 선호하며 항상 최종 콘크리트에서 인터페이스를 구현하는 것을 선호합니다. 제시 한 시나리오는 자주 발생하며 기본 인터페이스가 나를 귀찮게하는 이유의 일부입니다.
Bryan Boettcher

1
맞습니다. 인스 타와 저는 이런 방식으로 인터페이스를 사용하는 것은 많은 개발 환경에서 볼 수있는 상속 남용의 한 측면으로 간주합니다. GoF가 디자인 패턴 책에서 말했듯 이 상속보다 구성을 선호 하며 기본 클래스에서 인터페이스를 유지하는 것이 바로 그 원칙을 홍보하는 방법 중 하나입니다.
Mihai Danila

1
경고가 표시되지만 OP의 추상 클래스에는 인터페이스와 일치하는 추상 메소드가 포함되어 있습니다. BaseWorker는 암시 적으로 IFooWorker입니다. 명시 적으로 작성하면이 사실을보다 유용하게 사용할 수 있습니다. Queue에는 AbstractList에 포함되지 않은 수많은 메소드가 포함되어 있으므로 AbstractList는 자식이 될 수 있더라도 Queue가 아닙니다. AbstractList와 큐는 직교합니다.
Matthew Flynn

이 통찰력에 감사드립니다. 나는 OP의 사건이 그 범주에 속한다는 것에 동의하지만, 더 깊은 규칙을 찾기 위해 더 큰 토론이 있음을 느꼈고, 내가 읽은 경향에 대해 내 의견을 공유하고 싶었습니다. 아마도 OOP의 도구 상자에있는 도구 중 하나이기 때문에 상속을 과도하게 사용하는 것입니다.
Mihai Danila

덩, 6 년이 지났어. :)
Mihai Danila

2

IFooWorker새로운 방법 추가와 같이 변경하면 어떻게됩니까 ?

경우 BaseWorker인터페이스 구현, 기본적으로는 추상적 인 경우에도, 새로운 방법을 선언해야합니다. 반면, 인터페이스를 구현하지 않으면 파생 클래스에서 컴파일 오류 만 발생합니다.

이러한 이유로, 나는 기본 클래스가 인터페이스를 구현 할 것 I 이후, 수있는 파생 클래스를 건드리지 않고 기본 클래스의 새로운 방법에 대한 모든 기능을 구현 할 수 있습니다.


1

먼저 추상 기본 클래스가 무엇인지, 인터페이스가 무엇인지 생각하십시오. 둘 중 하나를 사용할 때와 사용하지 않을 때를 고려하십시오.

사람들이 매우 유사한 개념이라고 생각하는 것이 일반적이며, 실제로는 일반적인 인터뷰 질문입니다 (두 사람의 차이점은 무엇입니까 ??).

따라서 추상 기본 클래스는 인터페이스가 할 수없는 것을 제공합니다. 인터페이스는 기본 메소드 구현입니다. (C #에는 인터페이스가 아닌 추상 클래스에 정적 메소드를 가질 수 있습니다).

예를 들어, 이것의 일반적인 사용은 IDisposable입니다. abstract 클래스는 IDisposable의 Dispose 메소드를 구현합니다. 이는 기본 클래스의 파생 버전이 자동으로 일회용임을 의미합니다. 그런 다음 몇 가지 옵션으로 재생할 수 있습니다. 파생 클래스를 강제로 구현하여 Dispose abstract를 만듭니다. 가상 기본 구현을 제공하여 가상 또는 추상이 아닌 가상 구현을 제공하고 예를 들어 BeforeDispose, AfterDispose, OnDispose 또는 Disposing과 같은 가상 메소드를 호출하도록하십시오.

따라서 모든 파생 클래스가 인터페이스를 지원해야 할 때마다 기본 클래스로 진행됩니다. 하나 또는 일부만 해당 인터페이스가 필요한 경우 파생 클래스로 이동합니다.

그것은 모두 실제로 단순화에 대한 총체입니다. 또 다른 대안은 파생 클래스가 인터페이스를 구현하지 않고 어댑터 패턴을 통해 제공하는 것입니다. 내가 최근에 본 이것의 예는 IObjectContextAdapter에 있습니다.


1

나는 여전히 추상 클래스와 인터페이스의 차이점을 완전히 이해하지 못한다. 기본 개념을 다룰 때마다 stackexchange를 살펴보고 두 단계 뒤로 물러납니다. 그러나 주제와 OP 질문에 대한 몇 가지 생각 :

먼저:

인터페이스에 대한 일반적인 두 가지 설명이 있습니다.

  1. 인터페이스는 모든 클래스가 구현할 수있는 메서드 및 속성 목록이며, 인터페이스를 구현하면 클래스가 해당 클래스 또는 그 클래스의 객체. 인터페이스는 계약입니다.

  2. 인터페이스는 아무것도하지 않거나 할 수없는 추상 클래스입니다. 부모 클래스와 달리 둘 이상을 구현할 수 있기 때문에 유용합니다. 마찬가지로 BananaBread 클래스의 객체가 될 수 있고 BaseBread에서 상속 할 수 있지만 IWithNuts와 ITastesYummy 인터페이스를 모두 구현할 수는 없습니다. 빵이 아니기 때문에 IDoesTheDishes 인터페이스를 구현할 수도 있습니다.

추상 클래스에 대한 두 가지 일반적인 설명이 있습니다.

  1. 추상 클래스는, 할 수 있는 일이 아니라는 것입니다. 그것은 본질적으로 전혀 실제가 아닙니다. 잠시만 기다려주세요. 도움이됩니다. 보트는 추상 클래스이지만 섹시한 Playboy Yacht는 BaseBoat의 하위 클래스입니다.

  2. 나는 추상 수업에 관한 책을 읽었을 것입니다. 아마도 당신은 그 책을 읽지 않으면 아마도 그것을 얻지 못하고 잘못 할 것이기 때문에 그 책을 읽어야 할 것입니다.

그건 그렇고, Citers라는 책은 내가 여전히 혼란스럽게 걸어가더라도 항상 인상적으로 보입니다.

둘째:

그래서 누군가 가이 질문의 간단한 버전, 고전적인 "인터페이스를 사용하는 이유는 무엇입니까? 차이점은 무엇입니까? 한 답변은 공군 조종사를 간단한 예로 사용했습니다. 그것은 꽤 착륙하지는 않았지만 훌륭한 의견을 촉발했습니다.이 중 하나는 takeOff, pilotEject 등과 같은 메소드를 가진 IFlyable 인터페이스를 언급했습니다. 그러나 결정적입니다. 인터페이스는 객체 / 클래스를 직관적으로 만들거나 적어도 그 의미를 제공합니다. 인터페이스는 객체 나 데이터의 이점이 아니라 해당 객체와 상호 작용해야하는 무언가를위한 것입니다. 고전적인 과일-> 사과-> 후지 또는 모양-> 삼각형-> 상속의 등변 예는 주어진 자손을 기반으로 주어진 객체를 분류 학적으로 이해하기위한 훌륭한 모델입니다. 소비자와 프로세서에게 일반적인 특성, 동작, 객체가 사물 그룹인지 여부, 잘못된 장소에 시스템을 배치 할 때 시스템을 호스 핑하거나 감각 데이터, 특정 데이터 저장소에 대한 커넥터 또는 급여 지급에 필요한 재무 데이터.

특정 비행기 객체는 비상 착륙 방법이 있거나 없을 수 있지만, 비상 사태를 가정하면 DumbPlane으로 덮여 깨어나 개발자가 quickLand 그들은 모두 같다고 생각했기 때문입니다. 모든 나사 제조업체가 올바른 타이트에 대한 자체 해석을 가지고 있거나 TV에 볼륨 감소 버튼 위에 볼륨 증가 버튼이없는 경우 좌절감을 느끼는 것과 같습니다.

추상 클래스는 모든 하위 개체가 해당 클래스로 규정해야하는 항목을 설정하는 모델입니다. 오리의 모든 특성이 없다면 IQuack 인터페이스가 구현되어 있는지 여부는 중요하지 않습니다. 인터페이스는 다른 것을 확신 할 수없는 경우에도 의미가있는 것입니다. Jeff Goldblum과 Starbuck은 인터페이스가 상당히 유사하기 때문에 외계인 우주선을 비행 할 수있었습니다.

제삼:

때로는 처음에 특정 방법을 시행해야하기 때문에 동료에게 동의합니다. Active Record ORM을 작성하는 경우 저장 방법이 필요합니다. 인스턴스화 할 수있는 서브 클래스가 아닙니다. 또한 ICRUD 인터페이스가 하나의 추상 클래스에 독점적으로 연결될 수 없을 정도로 이식성이 좋으면 다른 클래스에 의해 구현되어 해당 추상 클래스의 하위 클래스에 이미 익숙한 모든 사람이 신뢰할 수 있고 직관적으로 만들 수 있습니다.

반면에 모든 목록 유형이 큐 인터페이스를 구현하지 않거나 구현해야하는 것은 아니기 때문에 인터페이스를 추상 클래스에 연결하지 않는 좋은 예가있었습니다. 이 시나리오는 절반의 시간이 걸리며, 이는 귀하와 동료 모두 절반의 시간이 잘못되었다는 것을 의미하므로 가장 좋은 방법은 논쟁, 토론, 고려 및 그들이 옳은 것으로 판명되면 커플 링을 인정하고 수락하는 것입니다 . 그러나 자신에게 가장 적합한 철학이 아니라도 철학을 따르는 개발자가되지 마십시오.


0

제안한 것처럼 DbWorker구현해야하는 또 다른 측면이 있습니다 IFooWorker.

동료 스타일의 경우, 어떤 이유로 나중에 리팩토링이 발생하고 DbWorker추상 BaseWorker클래스 를 확장하지 않는 것으로 간주 DbWorker되면 IFooWorker인터페이스 가 손실 됩니다. IFooWorker인터페이스 를 기대하는 경우 클라이언트를 소비하는 클라이언트에 영향을 줄 수 있지만 반드시 그런 것은 아닙니다 .


-1

먼저 인터페이스와 추상 클래스를 다르게 정의하고 싶습니다.

Interface: a contract that each class must fulfill if they are to implement that interface
Abstract class: a general class (i.e vehicle is an abstract class, while porche911 is not)

귀하의 경우 모든 근로자 work()가 계약에서 요구하는 것이므로 모든 근로자가 이행 합니다. 따라서 기본 클래스 Baseworker는 해당 메소드를 명시 적으로 또는 가상 함수로 구현해야합니다. 모든 작업자에게 필요한 간단한 사실 때문에이 방법을 기본 클래스에 넣는 것이 좋습니다 work(). 따라서 기본 클래스 (해당 유형의 포인터를 작성할 수는 없지만)에 함수로 포함시키는 것이 논리적입니다.

이제 인터페이스를 DbWorker만족 시키지만, IFooWorker이 클래스가하는 특정한 방법이 있다면 work(), 상속 된 정의를 오버로드해야합니다 BaseWorker. 인터페이스를 IFooWorker직접 구현하지 않아야하는 이유 는 이것이 특별한 것이 아니기 때문 DbWorker입니다. 당신이 비슷한 클래스를 구현할 때마다 그렇게하면 DRY를DbWorker 위반하게 됩니다.

두 클래스가 다른 방식으로 동일한 기능을 구현하면 가장 큰 공통 수퍼 클래스를 찾을 수 있습니다. 대부분의 경우 하나를 찾을 수 있습니다. 그렇지 않다면 계속 보거나 포기하고 기본 수업을 만들기에 공통점이 충분하지 않다는 것을 인정하십시오.


-3

와우,이 모든 대답과 예제의 인터페이스가 어떤 목적도 제공하지 않는다고 지적하지 않았습니다. 상속과 인터페이스를 같은 방법으로 사용하지 않으면 무의미합니다. 하나 또는 다른 것을 사용합니다. 이 포럼에는 각기 다른 장점을 설명하는 답변과 함께 많은 질문이 있습니다.


3
그것은 특허 허위입니다. 인터페이스는 구현에 관계없이 시스템이 작동 할 것으로 예상되는 계약입니다. 상속 된 기본 클래스는이 인터페이스에 가능한 많은 구현 중 하나입니다. 충분히 큰 규모의 시스템에는 다양한 인터페이스 (일반적으로 제네릭으로 닫힘)를 만족하는 여러 개의 기본 클래스가 있으며, 이는이 설정이 수행 한 작업과이를 분리하는 것이 도움이 된 이유입니다.
Bryan Boettcher

OOP 관점에서 볼 때 정말 나쁜 예입니다. "기본 클래스는 단지 하나의 구현"이라고 말하면서 (인터페이스에 대한 포인트가 없거나 없어야 함) IFooWorker 인터페이스를 구현하는 비 작업 클래스가있을 것입니다. 그런 다음 전문적인 fooworker 인 기본 클래스를 갖게되며 (바바 더도있을 수 있다고 가정해야 함) 기본 작업자가 아닌 다른 fooworker도 갖게됩니다! 거꾸로입니다. 여러 가지 방법으로.
Martin Maat

1
아마도 당신은 인터페이스에서 "Worker"단어에 갇혀있을 것입니다. "데이터 소스"는 어떻습니까? IDatasource <T>가 있으면 SqlDatasource <T>, RestDatasource <T>, MongoDatasource <T>를 가질 수 있습니다. 비즈니스는 제네릭 인터페이스의 어떤 클로저가 추상 데이터 소스의 클로징 구현을 결정합니다.
Bryan Boettcher 2016 년

DRY를 위해서만 상속을 사용하고 일반적인 구현을 기본 클래스에 넣는 것이 합리적입니다. 그러나 재정의 된 메소드를 인터페이스 메소드와 정렬하지 않으면 기본 클래스에 일반 지원 논리가 포함됩니다. 그들이 정렬되면 상속이 문제를 잘 해결하고 인터페이스가 목적을 달성하지 못한다는 것을 보여줍니다. 상속하려는 모델의 특성이 단일 클래스 클래스에 맞지 않기 때문에 상속이 문제를 해결하지 못하는 경우 인터페이스를 사용합니다. 그리고 그렇습니다.이 예의 문구는 더 잘못합니다.
Martin Maat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.