인터페이스 대베이스 클래스


767

언제 인터페이스를 사용해야하고 기본 클래스를 언제 사용해야합니까?

메소드의 기본 구현을 실제로 정의하지 않으려면 항상 인터페이스 여야합니까?

개와 고양이 수업이 있다면. PetBase 대신 IPet을 구현하고 싶은 이유는 무엇입니까? 애완 동물을 기준으로 애완 동물에 배치 할 수 있기 때문에 ISheds 또는 IBarks (IMakesNoise?)에 대한 인터페이스가 있음을 이해할 수 있지만 일반 애완 동물에 사용할 인터페이스를 이해하지 못합니다.


11
고려해야 할 점은 인터페이스가 매우 늦게 단계까지 인식하지 못할 수있는 몇 가지 한계를 가질 수 있다는 점입니다. 예를 들어 .NET을 사용하면 인터페이스 멤버 변수를 직렬화 할 수 없으므로 Zoo 클래스와 IAnimals의 멤버 변수 배열이 있으면 Zoo를 직렬화 할 수 없습니다 (즉, WebServices 또는 직렬화가 필요한 다른 항목은 고통).
synhershko 2014

1
이 질문은 인터페이스의 개념을 이해하는 데 도움이 될 수 있습니다. stackoverflow.com/q/8531292/1055241
gprathour

나는 단지 궁금하다. C #을 통해 CLR 에서 다음 발췌 부분을 만났습니다 I tend to prefer using the interface technique over the base type technique because the base type technique doesn’t allow the developer to choose the base type that works best in a particular situation.. 발췌문의 의미를 파악할 수 없습니다. 우리는 몇 가지 기본 유형을 생성하고 그 중 하나에 대한 파생 유형을 생성 할 수 있으므로 개발자는 기본 유형을 선택할 수 있습니다. 누군가 내가 무엇을 놓치고 있는지 설명해 주시겠습니까? 나는 이것이이 질문의 일부가 될 수 있다고 믿는다. 아니면 특정 발췌에 대해 다른 것을 게시해야합니까?
qqqqqqq

답변:


501

Dog 및 Cat 클래스의 예를 들어보고 C #을 사용하여 설명하겠습니다.

개와 고양이는 모두 동물, 특히 사족 포유류입니다 (동물은 너무 일반적입니다). 둘 다 추상 클래스 Mammal이 있다고 가정합니다.

public abstract class Mammal

이 기본 클래스에는 다음과 같은 기본 메소드가있을 것입니다.

  • 먹이다
  • 항해사

이들 모두는 어느 한 종 사이에서 거의 동일한 구현을 갖는 행동입니다. 이것을 정의하려면 다음이 필요합니다.

public class Dog : Mammal
public class Cat : Mammal

이제 우리가 보통 동물원에서 볼 수있는 다른 포유류가 있다고 가정 해 봅시다.

public class Giraffe : Mammal
public class Rhinoceros : Mammal
public class Hippopotamus : Mammal

기능의 핵심에서 여전히 유효 Feed()하며 Mate()여전히 동일 하기 때문에 이것은 여전히 ​​유효합니다 .

그러나 기린, 코뿔소, 하마는 애완 동물을 만들 수있는 동물이 아닙니다. 인터페이스가 유용한 곳입니다.

public interface IPettable
{
    IList<Trick> Tricks{get; set;}
    void Bathe();
    void Train(Trick t);
}

위 계약의 이행은 고양이와 개 사이에서 동일하지 않습니다. 상속을 위해 추상 클래스에 구현을 배치하는 것은 나쁜 생각입니다.

개와 고양이의 정의는 이제 다음과 같아야합니다.

public class Dog : Mammal, IPettable
public class Cat : Mammal, IPettable

이론적으로 상위 기본 클래스에서 재정의 할 수 있지만 기본적으로 인터페이스를 사용하면 상속 할 필요없이 클래스에 필요한 항목 만 추가 할 수 있습니다.

결과적으로 일반적으로 하나의 추상 클래스 (대부분 정적으로 유형이 지정된 OO 언어에서 예외는 C ++ 포함)에서만 상속 할 수 있지만 여러 인터페이스를 구현할 수 있기 때문에 필요한만큼 엄격하게 객체를 구성 할 수 있습니다 .


149
나는 그것이 그렇게 간단하다고 생각하지 않습니다. 인터페이스가 더 이해되도록 질문 (요구 사항)을 약간 변경했습니다. 계약 (인터페이스) 또는 공유 구현 (기본 클래스)을 정의하고 있는지 항상 스스로에게 문의해야합니다.
David Pokluda

18
인터페이스는 계약입니다. 서비스에서 요구하는 계약의 일부만 노출하고 있습니다. 'PettingZoo'가 있다면 'Mate'-ing을 사용자에게 노출하고 싶지는 않습니다.
Anthony Mastrean 2009 년

10
@David Touche, 인터페이스가 무엇인지, 그리고 그의 이해에 대한 추상 클래스가 무엇인지 더 잘 설명하기 위해 노력했지만. 개와 고양이는 엄격한 요구 사항이 아닙니다!
Jon Limjap

2
해석 된 JIT 환경 (특히 JVM)에서 가상 메소드 호출은 컨텍스트에 따라 인터페이스 메소드 호출보다 훨씬 빠릅니다 . JVM이 느린 메소드 검색을 최적화 할 수 있기 때문에 "컨텍스트"를 강조합니다. (예를 들어, 인터페이스 상속자 목록이 같은 클래스의 인스턴스 인 경우 슬프게도 벤치마킹이 어려워집니다.) 성능에 민감한 무언가를 최적화하려는 경우에만 고려해야합니다.
Philip Guin

14
또한 인터페이스는 애완 동물 바위를 허용하여 목욕과 트릭을 가르 칠 수 있지만 먹이와 짝짓기는 지원되지 않습니다.
eclux 2016

146

Josh Bloch는 Effective Java 2d 에서 자신을 말했습니다 .

추상 클래스보다 인터페이스 선호

몇 가지 주요 사항 :

  • 기존 클래스를 쉽게 개조하여 새로운 인터페이스를 구현할 수 있습니다 . 필요한 메소드가 아직 없으면 추가하고 클래스 선언에 구현 절을 추가하기 만하면됩니다.

  • 인터페이스는 믹스 인을 정의하는 데 이상적입니다 . 느슨하게 말해서, 믹스 인은 클래스가“기본 유형”외에 구현할 수있는 유형으로, 선택적인 동작을 제공한다고 선언합니다. 예를 들어 Comparable은 클래스가 서로 비교 가능한 다른 객체와 관련하여 해당 인스턴스의 순서를 선언 할 수있는 mixin 인터페이스입니다.

  • 인터페이스를 통해 비 계층 형식 프레임 워크를 구성 할 수 있습니다. 유형 계층 구조는 어떤 것을 구성하는 데 유용하지만 다른 것은 깔끔한 계층 구조로 깔끔하게 분류되지 않습니다.

  • 인터페이스 는 랩퍼 클래스 관용구를 통해 안전하고 강력한 기능 향상가능하게합니다 . 추상 클래스를 사용하여 유형을 정의하는 경우 대체 기능없이 기능을 추가하려는 프로그래머가 상속을 사용하지 않아도됩니다.

또한 추상 스켈 레탈 구현 클래스를 제공하여 내보내는 각각의 중요하지 않은 인터페이스와 함께 인터페이스와 추상 클래스의 장점을 결합 할 수 있습니다.

반면에 인터페이스는 진화하기가 매우 어렵습니다. 인터페이스에 메소드를 추가하면 모든 구현이 중단됩니다.

추신. : 책을 구입하십시오. 훨씬 더 자세합니다.


71
인터페이스를 변경해야 할 때마다 변경되지 않는 방법은 이전 인터페이스에서 상속되는 새 인터페이스를 만드는 것입니다. 이렇게하면 기존 구현이 유지되고 새 구현에서 원하는대로 수행 할 수 있습니다.
Scott Lawrence

5
"인터페이스 분리 원리"가 있습니다. 이 원칙은 인터페이스 작성 방법에주의를 기울 이도록 지시합니다. 인터페이스를 작성할 때는 반드시 있어야하는 메소드 만 추가해야합니다. 존재하지 않아야하는 메소드를 추가하면 인터페이스를 구현하는 클래스도 해당 메소드를 구현해야합니다. 예를 들어 Worker라는 인터페이스를 만들고 점심 식사 방법을 추가하면 모든 작업자가이를 구현해야합니다. 작업자가 로봇 인 경우 어떻게해야합니까 결론 결론적으로 특정되지 않은 메서드가 포함 된 인터페이스를 오염 또는 팻 인터페이스라고합니다.
Eklavyaa

Java 8부터 기본 메소드를 사용하면 인터페이스에 새 기능을 추가하고 해당 인터페이스를 구현하는 기존 클래스와의 호환성을 보장 할 수 있습니다. 기본 메소드는 구현 클래스에서 대체되지 않은 경우 기본적으로 호출됩니다. 모든 구현 클래스 중 하나를 기본 방법을 대체 할 수 있습니다 또는 직접 () instance.defaultMethod를 사용하여 호출 할 수 있습니다
Arpit Rastogi

118

인터페이스와 기본 클래스는 서로 다른 두 가지 형태의 관계를 나타냅니다.

상속 (기본 클래스)은 "is-a"관계를 나타냅니다. 예를 들어 개 또는 고양이 "is-a"애완 동물. 이 관계는 항상 "단일 책임 원칙" 과 함께 클래스 의 (단일) 목적 을 나타냅니다 .

반면에 인터페이스 는 클래스의 추가 기능 을 나타냅니다 . " Foois disposable" 과 같이 "is"관계라고 부르 므로 IDisposableC # 의 인터페이스입니다.


10
모든 답변 중이 답변은 명료성을 잃지 않으면 서 최고의 간결함을 제공합니다.
Matt Wilko

누군가가 "has-a"관계가있을 때 인터페이스를 사용하라고 말했습니다. 그래도 그것이 항상 사실인지 잘 모르겠습니다. 랩톱에 화면이 있으므로 랩톱에 IScreen을 구현해야합니까? 아니면 Screen 속성이 있어야합니까? 후자는 나에게 더 자연스러워 보인다.
Berend

1
@berend 당신이 쓰는 재미 있다는 것을 스크린 인터페이스를 통해 구현되기 때문에 - VGA, HDMI 등
콘스탄틴

OOP이기 때문에 실제 시나리오를 따르기 위해 상상력을 확장해야합니다. 항상 적용되는 것은 아닙니다.
Crismogram

110

현대적인 스타일은 IPet PetBase 를 정의하는 것입니다.

인터페이스의 장점은 다른 코드가 다른 실행 코드와 아무 관련없이 사용할 수 있다는 것입니다. 완전히 "깨끗합니다." 또한 인터페이스를 혼합 할 수 있습니다.

그러나 기본 클래스는 간단한 구현 및 공통 유틸리티에 유용합니다. 따라서 시간과 코드를 절약하기 위해 추상 기본 클래스를 제공하십시오.


케이크를 가지고 먹어!
Darren Kopp

3
인터페이스는 다른 클래스가 코드를 사용하는 방법을 정의합니다. 기본 클래스는 구현자가 인터페이스를 구현하는 데 도움이됩니다. 두 가지 목적을위한 두 가지 다른 것.
Bill Michell

27
여기에는 "현대적인"것이 없습니다. 동일한 API를 사용하는 기본 클래스 및 인터페이스는 단순히 중복됩니다. 경우에 따라이 방법을 사용할 수도 있지만 일반화해서는 안됩니다!
smentek

3
두 번째 지원되는 답변에 대해 가장 많이 지원되는 의견은 실제로 흥미로운 답변 자체에 동의하지 않습니다. 인터페이스와 기본 클래스의 많은 예가 공존한다는 것을 말해야합니다. 그런 의미에서 그것은 "현대적인"방법입니다. 예를 들어 MVVM 패턴에는 실제로 INotifyPropertyChanged를 구현하는 ViewModelBase 클래스가 있습니다. 내가 대신 모든보기 모델의 인터페이스를 구현하는 기본 클래스가 않은 이유 내 동료가 나에게 물었을 때, 나는 그를 설득하는 방법을 모른다

1
옳은. 1 또는 다른 질문은 아닙니다. 그것들은 매우 다른 두 가지 문제를 해결하기 위해 존재합니다. 인터페이스는 구현 클래스가 해결해야하는 계약입니다. 그들은 IoC 및 TDD 측면에 대한 최근의 호의 (때로는 광신)를 발견했습니다. 추상 / 기본 클래스는 계층 적으로 일반적인 논리 및 속성을 그룹화하는 역할을합니다. 중복 코드를 줄여 솔루션 유지 관리 성을 높이고 오류 발생 가능성을 줄입니다.
ComeIn

63

인터페이스

  • 두 모듈 간의 계약을 정의합니다. 구현할 수 없습니다.
  • 대부분의 언어를 사용하면 여러 인터페이스를 구현할 수 있습니다
  • 인터페이스 수정은 주요 변경 사항입니다. 모든 구현을 다시 컴파일 / 수정해야합니다.
  • 모든 회원은 공개됩니다. 구현은 모든 멤버를 구현해야합니다.
  • 인터페이스는 디커플링에 도움이됩니다. 모의 프레임 워크를 사용하여 인터페이스 뒤의 모든 것을 모의 할 수 있습니다.
  • 인터페이스는 일반적으로 일종의 동작을 나타냅니다.
  • 인터페이스 구현은 서로 분리 / 분리

기본 수업

  • 파생을 통해 무료로 제공되는 기본 구현 을 추가 할 수 있습니다
  • C ++를 제외하고 하나의 클래스에서만 파생 될 수 있습니다. 여러 클래스에서 가능하더라도 일반적으로 나쁜 생각입니다.
  • 기본 클래스를 변경하는 것은 비교적 쉽습니다. 파생물은 특별한 것을 할 필요가 없습니다.
  • 기본 클래스는 파생으로 액세스 할 수있는 보호 된 공용 함수를 선언 할 수 있습니다.
  • 인터페이스처럼 추상 기본 클래스를 쉽게 조롱 할 수 없습니다
  • 기본 클래스는 일반적으로 유형 계층 구조를 나타냅니다 (IS A)
  • 클래스 파생은 일부 기본 동작 (부모 구현에 대한 복잡한 지식이 있음)에 따라 달라질 수 있습니다. 한 사람의 기본 구현을 변경하고 다른 사람을 중단하면 문제가 발생할 수 있습니다.

참고 : 프레임 워크 디자인 가이드 라인은 기본 클래스 (인터페이스가 아닌)를 사용하는 것이 좋습니다. vNext의 추상 기본 클래스에 새로운 메소드를 추가하는 것은 끊임없는 변화입니다.
Gishu

59

일반적으로 추상 클래스보다 인터페이스를 선호해야합니다. 추상 클래스를 사용하는 한 가지 이유는 구체적인 클래스간에 공통 구현이있는 것입니다. 물론 여전히 인터페이스 (IPet)를 선언하고 해당 인터페이스를 구현하는 추상 클래스 (PetBase)가 있어야합니다. 인터페이스는 경계를 넘어서 유형의 유연성과 이식성을 극대화합니다. 경계를 넘어 참조를 전달할 때는 항상 콘크리트 유형이 아닌 인터페이스를 전달하십시오. 이를 통해 수신 측에서 구체적인 구현을 결정하고 최대한의 유연성을 제공합니다. 이것은 TDD / BDD 방식으로 프로그래밍 할 때 절대적으로 적용됩니다.

Gang of Four는 자신의 저서에서 "상속은 서브 클래스가 부모의 구현에 대한 세부 사항에 노출되기 때문에 종종"상속이 캡슐화를 깨뜨린 다 "고 말합니다. 나는 이것이 사실이라고 믿는다.


예쉬 개인적으로, 나는 이것이 거꾸로 믿습니다. 인터페이스는 형식에 대한 최소한의 기능을 유지해야하며 기본 클래스는 사용자 지정을 구성 할 수있는 풍부한 프레임 워크를 제공해야합니다. 인터페이스에 넣으면 구현하기가 매우 어려워졌습니다.

아무도 인터페이스가 커야한다고 말하지 않았습니다. 더 작고 다양한 인터페이스와 더 많은 기본 클래스가 훌륭한 API를 만듭니다.
Kilhoffer

3
그냥 나인가요, 아니면 대부분의 "공통 작업자"클래스가 공통 구현을 공유합니까? 이러한 맥락에서 인터페이스를 선호하는 일반적인 규칙에 위배됩니다. 일반화를 2 가지 일반화로 다시 설명하겠습니다. 논리가 없거나 거의없는 공통 클래스는 인터페이스를 구현해야합니다. "괜찮은"양의 논리를 포함하는 공통 클래스는 기본 클래스에서 파생되어야합니다 (대부분 기능을 공유 할 것이므로).
goku_da_master

@Kilhoffer "인터페이스는 경계를 넘어서 유형의 유연성과 이식성을 최대한으로 허용합니다."이 문장을 자세히 설명하십시오.
JAVA

49

이것은 .NET에 따라 다르지만 Framework Design Guidelines 책은 일반적인 클래스에서 진화하는 프레임 워크에서 더 많은 유연성을 제공한다고 주장합니다. 인터페이스가 제공되면 해당 인터페이스를 사용하는 코드를 깨지 않고도 인터페이스를 변경할 수 없습니다. 그러나 클래스를 사용하면 클래스를 수정하고 링크 된 코드를 중단하지 않아도됩니다. 새로운 기능 추가를 포함하여 올바른 수정을하면 코드를 확장하고 발전시킬 수 있습니다.

Krzysztof Cwalina는 81 페이지에 말합니다.

.NET Framework의 세 가지 버전을 통해이 가이드 라인에 대해 팀의 많은 개발자와 이야기했습니다. 처음에 지침에 동의하지 않은 사람들을 포함하여 많은 사람들은 인터페이스로 일부 API를 제공 한 것을 후회한다고 말했습니다. 누군가가 수업을 보낸 것을 후회 한 사례조차 들어 본 적이 없습니다.

거기에는 분명히 인터페이스의 장소가 있습니다. 일반적인 지침으로 인터페이스를 구현하는 방법의 예로서 다른 방법이 없다면 항상 인터페이스의 추상 기본 클래스 구현을 제공하십시오. 가장 좋은 경우에 기본 클래스는 많은 작업을 절약 할 수 있습니다.


19

후안,

인터페이스를 클래스를 특징 짓는 방법으로 생각하고 싶습니다. YorkshireTerrier와 같은 특정 개 품종 클래스는 부모 개 클래스의 자손 일 수 있지만 IFurry, IStubby 및 IYippieDog도 구현합니다. 따라서 클래스는 클래스가 무엇인지 정의하지만 인터페이스는 클래스에 대해 알려줍니다.

이것의 장점은 예를 들어 모든 IYippieDog를 모아서 오션 컬렉션에 넣을 수 있다는 것입니다. 이제 특정 개체 집합에 도달하여 클래스를 너무 면밀히 검사하지 않고도 내가보고있는 기준에 맞는 개체를 찾을 수 있습니다.

인터페이스가 실제로 클래스의 공개 행동의 하위 세트를 정의해야한다는 것을 알았습니다. 구현하는 모든 클래스에 대한 모든 공개 동작을 정의하면 일반적으로 존재하지 않아도됩니다. 그들은 나에게 유용한 것을 말하지 않습니다.

이 생각은 모든 클래스에 인터페이스가 있어야하며 인터페이스에 코딩해야한다는 생각과 상반됩니다. 괜찮지 만 클래스에 대한 일대일 인터페이스가 많기 때문에 혼란스러워집니다. 아이디어는 실제로 비용이 들지 않으며 이제는 물건을 쉽게 교환 할 수 있다는 것을 이해합니다. 그러나 나는 거의 그렇게하지 않습니다. 대부분의 경우 기존 클래스를 수정하고 해당 클래스의 공용 인터페이스를 변경해야하는 경우와 동일한 문제가 발생합니다. 단, 두 위치에서 변경해야합니다.

당신이 나처럼 생각한다면 당신은 분명히 고양이와 개가 IPettable이라고 말할 것입니다. 둘 다 일치하는 특성입니다.

이것의 다른 부분은 동일한 기본 클래스를 가져야합니까? 문제는 그것들이 광범위하게 같은 것으로 취급되어야한다는 것입니다. 확실히 그들은 둘 다 동물이지만, 우리가 그것들을 함께 사용하는 방법에 맞습니다.

모든 동물 클래스를 모아서 방주 용기에 넣고 싶다고 가정 해보십시오.

아니면 포유류가되어야합니까? 아마도 우리는 동물 간 착유 공장이 필요합니까?

그들은 심지어 서로 연결되어야합니까? 둘 다 IPettable이라는 것을 아는 것으로 충분합니까?

나는 실제로 하나의 클래스가 필요할 때 전체 클래스 계층 구조를 도출하려는 욕구를 느낍니다. 나는 언젠가 그것을 필요로 할 것으로 예상하고 일반적으로 결코하지 않습니다. 내가 할 때조차도, 나는 보통 그것을 고치려면 많은 일을해야한다는 것을 알게된다. 내가 만들고있는 첫 번째 수업은 개가 아니기 때문에 운이 좋지 않습니다. 대신 오리너구리입니다. 이제 전체 클래스 계층 구조는 기괴한 경우를 기반으로하며 많은 낭비 된 코드가 있습니다.

어떤 지점에서는 모든 고양이가 IPetable이 아니라는 것을 알 수 있습니다 (예 : 털없는 고양이). 이제 해당 인터페이스를 적합한 모든 파생 클래스로 이동할 수 있습니다. 갑작스러운 고양이가 PettableBase에서 더 이상 파생되지 않는 훨씬 더 적은 변화를 발견 할 것입니다.


18

인터페이스와 기본 클래스의 기본적이고 간단한 정의는 다음과 같습니다.

  • 기본 클래스 = 객체 상속.
  • 인터페이스 = 기능 상속.

건배


12

가능하면 상속 대신 구성을 사용하는 것이 좋습니다. 인터페이스를 사용하지만 기본 구현에는 멤버 객체를 사용하십시오. 이렇게하면 특정 방식으로 동작하도록 객체를 구성하는 팩토리를 정의 할 수 있습니다. 동작을 변경하려면 다른 유형의 하위 오브젝트를 작성하는 새로운 팩토리 메소드 (또는 추상 팩토리)를 작성하십시오.

경우에 따라 변경 가능한 모든 동작이 도우미 개체에 정의되어 있으면 기본 개체에 인터페이스가 전혀 필요하지 않을 수 있습니다.

따라서 IPet 또는 PetBase 대신 IFurBehavior 매개 변수가있는 Pet로 끝날 수 있습니다. IFurBehavior 매개 변수는 PetFactory의 CreateDog () 메서드로 설정됩니다. 이 매개 변수는 shed () 메소드에 대해 호출됩니다.

이 작업을 수행하면 코드가 훨씬 유연하고 대부분의 간단한 객체가 시스템 전체의 기본적인 동작을 처리 할 수 ​​있습니다.

다중 상속 언어에서도이 패턴을 권장합니다.


12

Java World 기사에 잘 설명되어 있습니다 .

개인적으로 인터페이스를 사용하여 인터페이스를 정의하는 경향이 있습니다. 즉, 어떤 방식으로 액세스해야하는지 지정하는 시스템 설계의 일부입니다.

하나 이상의 인터페이스를 구현하는 클래스를 갖는 것은 드문 일이 아닙니다.

다른 클래스의 기초로 사용하는 추상 클래스.

다음은 위에서 언급 한 기사 JavaWorld.com 기사, 저자 Tony Sintes, 04/20/01에서 발췌 한 것입니다.


인터페이스와 추상 클래스

인터페이스와 추상 클래스를 선택하는 것은 하나의 제안이 아닙니다. 디자인을 변경해야하는 경우 인터페이스로 만드십시오. 그러나 기본 동작을 제공하는 추상 클래스가있을 수 있습니다. 추상 클래스는 응용 프로그램 프레임 워크 내에서 훌륭한 후보입니다.

추상 클래스를 사용하면 일부 동작을 정의 할 수 있습니다. 그들은 당신의 서브 클래스가 다른 사람들을 제공하도록 강요합니다. 예를 들어, 응용 프로그램 프레임 워크가있는 경우 추상 클래스는 이벤트 및 메시지 처리와 같은 기본 서비스를 제공 할 수 있습니다. 이러한 서비스를 통해 애플리케이션을 애플리케이션 프레임 워크에 플러그인 할 수 있습니다. 그러나 응용 프로그램 만 수행 할 수있는 응용 프로그램 별 기능이 있습니다. 이러한 기능에는 종종 응용 프로그램에 따라 시작 및 종료 작업이 포함될 수 있습니다. 따라서 추상 기본 클래스는 해당 동작 자체를 정의하는 대신 추상 종료 및 시작 메소드를 선언 할 수 있습니다. 기본 클래스는 해당 메소드가 필요하다는 것을 알고 있지만 추상 클래스는 클래스가 해당 조치를 수행하는 방법을 모른다고 인정합니다. 작업을 시작해야한다는 것만 알고 있습니다. 시작할 때가되면 추상 클래스는 startup 메소드를 호출 할 수 있습니다. 기본 클래스가이 메소드를 호출하면 Java는 하위 클래스로 정의 된 메소드를 호출합니다.

많은 개발자들은 추상 메소드를 정의하는 클래스가 해당 메소드를 호출 할 수 있다는 것을 잊어 버립니다. 추상 클래스는 계획된 상속 계층을 만드는 훌륭한 방법입니다. 또한 클래스 계층에서 비 리프 클래스에 적합합니다.

클래스와 인터페이스

일부는 인터페이스 측면에서 모든 클래스를 정의해야한다고 말하지만 권장 사항은 약간 극단적이라고 생각합니다. 디자인의 내용이 자주 바뀔 때 인터페이스를 사용합니다.

예를 들어, 전략 패턴을 사용하면 새 알고리즘과 프로세스를 사용하는 개체를 변경하지 않고 프로그램으로 새 알고리즘과 프로세스를 바꿀 수 있습니다. 미디어 플레이어는 CD, MP3 및 wav 파일을 재생하는 방법을 알고있을 것입니다. 물론 재생 알고리즘을 플레이어에 하드 코딩하고 싶지는 않습니다. AVI와 같은 새로운 형식을 추가하기가 어렵습니다. 또한 코드에 쓸모없는 사례가 흩어져 있습니다. 또한 부상을 입히려면 새 알고리즘을 추가 할 때마다 해당 사례를 업데이트해야합니다. 대체로 이것은 프로그래밍을위한 객체 지향 방식이 아닙니다.

전략 패턴을 사용하면 객체 뒤에 알고리즘을 간단히 캡슐화 할 수 있습니다. 그렇게하면 언제든지 새 미디어 플러그인을 제공 할 수 있습니다. 플러그인 클래스 MediaStrategy를 호출 해 봅시다. 이 객체에는 playStream (Stream s)이라는 하나의 메소드가 있습니다. 따라서 새로운 알고리즘을 추가하기 위해 단순히 알고리즘 클래스를 확장합니다. 이제 프로그램이 새로운 미디어 유형을 만나면 단순히 스트림 재생을 미디어 전략에 위임합니다. 물론 필요한 알고리즘 전략을 올바르게 인스턴스화하려면 배관이 필요합니다.

인터페이스를 사용하기에 좋은 곳입니다. 우리는 전략 패턴을 사용했는데, 이는 디자인에서 변화 할 장소를 분명히 나타냅니다. 따라서 전략을 인터페이스로 정의해야합니다. 일반적으로 객체가 특정 유형을 가지려면 상속보다 인터페이스를 선호해야합니다. 이 경우에는 MediaStrategy입니다. 타입 아이덴티티에 대한 상속에 의존하는 것은 위험하다; 특정 상속 계층 구조로 고정합니다. Java는 다중 상속을 허용하지 않으므로 유용한 구현 또는 더 많은 유형 ID를 제공하는 것을 확장 할 수 없습니다.


2
+1. "유형 아이덴티티에 대한 상속에 의존하는 것은 위험하다; 그것은 특정 상속 계층 구조에 잠겨있다." 이 문장은 인터페이스를 선호하는 이유를 완벽하게 설명합니다.
엔지니어

그리고 그 이상으로 확장하는 대신 인터페이스의 각 메소드 뒤에 구현을 구성하십시오.
엔지니어

10

또한 OO에서 휩쓸 리지 말고 ( 블로그 참조 ) 항상 필요한 동작을 기반으로 객체를 모델링 하십시오. 필요한 동작이 동물의 일반적인 이름과 종인 앱을 디자인하는 경우에는 필요한 경우에만 필요합니다. 세계에서 가능한 모든 동물에 대한 수백만 개의 클래스 대신 이름을 가진 하나의 클래스 동물.


10

나는 대략 엄지 손가락의 규칙이있다

기능 : 모든 부분에서 다를 수 있습니다. 인터페이스.

데이터와 기능, 파트는 대부분 동일하며 파트가 다릅니다 : 추상 클래스.

약간의 변경만으로 확장 된 경우 실제로 작동하는 데이터 및 기능 : 일반 (콘크리트) 클래스

데이터 및 기능, 변경 계획 없음 : 최종 수정자가 포함 된 일반 (콘크리트) 클래스.

데이터 및 기능 : 읽기 전용 : 열거 형 멤버

이것은 매우 거칠고 준비가되어 있고 엄격하게 정의되어 있지는 않지만 모든 것이 읽기 전용 파일처럼 약간 고정 된 열거 형으로 변경되는 인터페이스의 스펙트럼이 있습니다.


7

인터페이스는 작아야합니다. 정말 작습니다. 실제로 객체를 분해하는 경우 인터페이스에는 몇 가지 매우 구체적인 메소드와 속성 만 포함됩니다.

추상 클래스는 바로 가기입니다. PetBase의 모든 파생물이 공유하여 한 번 코딩하고 수행 할 수있는 것들이 있습니까? 그렇다면 추상 수업 시간입니다.

추상 클래스도 제한적입니다. 그것들은 당신에게 자식 객체를 생성하는 훌륭한 지름길을 제공하지만 주어진 객체는 하나의 추상 클래스 만 구현할 수 있습니다. 여러 번, 나는 이것이 추상 클래스의 한계를 발견하고 이것이 많은 인터페이스를 사용하는 이유입니다.

추상 클래스에는 여러 인터페이스가 포함될 수 있습니다. 귀하의 PetBase 추상 클래스는 IPet (애완 동물 소유자가 있음) 및 IDigestion (애완 동물이 먹거나 최소한 먹어야 함)을 구현할 수 있습니다. 그러나 PetBase는 IMammal을 구현하지 않을 것입니다. 모든 애완 동물이 포유 동물이 아니고 모든 포유 동물이 애완 동물이 아니기 때문입니다. PetBase를 확장하고 IMammal을 추가하는 MammalPetBase를 추가 할 수 있습니다. FishBase는 PetBase를 가지고 IFish를 추가 할 수 있습니다. IFish는 ISwim과 IUnderwaterBreather를 인터페이스로 사용합니다.

그렇습니다. 제 예제는 간단한 예제에 비해 지나치게 복잡하지만 인터페이스와 추상 클래스가 함께 작동하는 방법에 대한 큰 부분입니다.


7

출처 : http://jasonroell.com/2014/12/09/interfaces-vs-abstract-classes-what-should-you-use/

C #은 지난 14 년 동안 발전하고 발전한 훌륭한 언어입니다. 성숙한 언어는 우리에게 많은 언어 기능을 제공하기 때문에 개발자에게 좋습니다.

그러나 많은 힘을 가진 것은 큰 책임이됩니다. 이러한 기능 중 일부는 잘못 사용되거나 때로는 한 기능을 다른 기능보다 사용하기로 선택한 이유를 이해하기 어렵습니다. 수년 동안 많은 개발자들이 어려움을 겪고있는 기능은 인터페이스를 사용하거나 추상 클래스를 사용하도록 선택할 때입니다. 둘 다 장단점이 있으며 각각을 사용할 정확한 시간과 장소가 있습니다. 그러나 우리는 어떻게 결정합니까 ???

둘 다 유형 간 공통 기능을 재사용 할 수 있습니다. 가장 명백한 차이점은 인터페이스가 기능에 대한 구현을 제공하지 않는 반면 추상 클래스를 사용하면 일부 "기본"또는 "기본"동작을 구현 한 다음 필요한 경우 클래스 파생 유형으로이 기본 동작을 "재정의"할 수 있다는 것입니다. .

이것은 모두 훌륭하고 코드를 크게 재사용하고 소프트웨어 개발의 DRY (Do n't Repeat Yourself) 원칙을 준수합니다. 추상 클래스는“is a”관계가있을 때 사용하기 좋습니다.

예를 들어 : 골든 리트리버는 "개"유형의 개입니다. 푸들도 마찬가지입니다. 그들은 모든 개가 할 수 있듯이 짖을 수 있습니다. 그러나 푸들 파크가 "기본"개 껍질과 크게 다르다고 말하고 싶을 수도 있습니다. 따라서 다음과 같이 구현하는 것이 좋습니다.

public abstract class Dog
{
      public virtual void Bark()
      {
        Console.WriteLine("Base Class implementation of Bark");
      }
}

public class GoldenRetriever : Dog
{
   // the Bark method is inherited from the Dog class
}

public class Poodle : Dog
{
  // here we are overriding the base functionality of Bark with our new implementation
  // specific to the Poodle class
  public override void Bark()
  {
     Console.WriteLine("Poodle's implementation of Bark");
  }
}

// Add a list of dogs to a collection and call the bark method.

void Main()
{
    var poodle = new Poodle();
    var goldenRetriever = new GoldenRetriever();

    var dogs = new List<Dog>();
    dogs.Add(poodle);
    dogs.Add(goldenRetriever);

    foreach (var dog in dogs)
    {
       dog.Bark();
    }
}

// Output will be:
// Poodle's implementation of Bark
// Base Class implementation of Bark

// 

보시다시피, 이것은 코드를 DRY로 유지하고 유형 중 하나가 특수 사례 구현 대신 기본 Bark에 의존 할 때 기본 클래스 구현을 호출 할 수있는 좋은 방법입니다. GoldenRetriever, Boxer, Lab과 같은 클래스는 Dog abstract 클래스를 구현하기 때문에 "기본"(베이스 클래스) Bark를 무료로 상속 할 수 있습니다.

그러나 나는 당신이 이미 그것을 알고 있다고 확신합니다.

추상 클래스를 통해 인터페이스를 선택하려는 이유를 이해하고 싶거나 그 반대의 이유도 여기에 있습니다. 추상 클래스를 통해 인터페이스를 선택해야하는 한 가지 이유는 기본 구현이 없거나 방지하려는 경우입니다. 일반적으로 인터페이스를 구현하는 유형은 "is a"관계와 관련이 없기 때문입니다. 실제로, 그들은 각 유형이 무언가를 할 수 있거나 무언가를 가질 수있는 능력이 있다는 것을 제외하고는 전혀 관련 될 필요가 없습니다.

도대체 그게 무슨 뜻입니까? 예를 들어, 인간은 오리가 아니며 오리는 인간이 아닙니다. 꽤 분명합니다. 그러나 오리와 인간 모두 수영 할 수있는 능력이 있습니다 (1 학년에 인간이 수영 수업을 통과 한 경우). 또한 오리는 인간이 아니거나 그 반대이기 때문에 이것은“현상”이 아니라“유능한”관계이며 우리는이를 설명하기 위해 인터페이스를 사용할 수 있습니다.

// Create ISwimable interface
public interface ISwimable
{
      public void Swim();
}

// Have Human implement ISwimable Interface
public class Human : ISwimable

     public void Swim()
     {
        //Human's implementation of Swim
        Console.WriteLine("I'm a human swimming!");
     }

// Have Duck implement ISwimable interface
public class Duck: ISwimable
{
     public void Swim()
     {
          // Duck's implementation of Swim
          Console.WriteLine("Quack! Quack! I'm a Duck swimming!")
     }
}

//Now they can both be used in places where you just need an object that has the ability "to swim"

public void ShowHowYouSwim(ISwimable somethingThatCanSwim)
{
     somethingThatCanSwim.Swim();
}

public void Main()
{
      var human = new Human();
      var duck = new Duck();

      var listOfThingsThatCanSwim = new List<ISwimable>();

      listOfThingsThatCanSwim.Add(duck);
      listOfThingsThatCanSwim.Add(human);

      foreach (var something in listOfThingsThatCanSwim)
      {
           ShowHowYouSwim(something);
      }
}

 // So at runtime the correct implementation of something.Swim() will be called
 // Output:
 // Quack! Quack! I'm a Duck swimming!
 // I'm a human swimming!

위의 코드와 같은 인터페이스를 사용하면 객체를 "할 수있는"메소드로 객체를 전달할 수 있습니다. 코드는 그 방법을 신경 쓰지 않습니다 ... 알고있는 것은 해당 객체에서 Swim 메소드를 호출 할 수 있으며 해당 객체는 유형에 따라 런타임에 어떤 동작이 수행되는지 알 것입니다.

다시 한 번, 이것은 코드가 DRY 상태를 유지하도록하여 동일한 핵심 함수 (ShowHowHumanSwims (human), ShowHowDuckSwims (duck) 등)를 수행하기 위해 객체를 호출하는 여러 메소드를 작성할 필요가 없도록합니다.

여기에서 인터페이스를 사용하면 호출하는 메소드가 어떤 유형이 어떤 또는 어떻게 동작이 구현되는지에 대해 걱정할 필요가 없습니다. 인터페이스가 주어지면 각 객체가 Swim 메소드를 구현해야하므로 자체 코드로 호출하는 것이 안전하고 Swim 메소드의 동작을 자체 클래스 내에서 처리 할 수 ​​있습니다.

요약:

따라서 제 기본 규칙은 클래스 계층 구조 및 / 또는 작업중인 클래스 또는 유형에 "기본"기능을 구현하려는 경우 추상 클래스를 사용하는 것입니다 (예 : 푸들 " ”유형의 개).

반면에“is a”관계는 없지만 무언가를 할 수있는 능력을 공유하는 유형 (예 : Duck은 인간이 아님) 인 경우 인터페이스를 사용하십시오. "수영 능력").

추상 클래스와 인터페이스의 또 다른 차이점은 클래스는 일대 다 인터페이스를 구현할 수 있지만 클래스는 하나의 추상 클래스 (또는 해당 문제에 대한 모든 클래스)에서만 상속 할 수 있다는 것입니다. 예, 클래스를 중첩하고 상속 계층 구조를 가질 수 있지만 (많은 프로그램이 갖고 있어야 함) 하나의 파생 클래스 정의에서 두 클래스를 상속 할 수 없습니다 (이 규칙은 C #에 적용됩니다. 다른 언어에서는 일반적으로이를 수행 할 수 있음) 이 언어의 인터페이스가 부족하기 때문에).

또한 인터페이스를 사용하여 인터페이스 분리 원칙 (ISP)을 준수 할 때도 기억하십시오. ISP는 클라이언트가 사용하지 않는 방법에 의존해서는 안된다고 명시하고 있습니다. 이러한 이유로 인터페이스는 특정 작업에 중점을 두어야하며 일반적으로 매우 작습니다 (예 : IDisposable, IComparable).

또 다른 팁은 작고 간결한 기능을 개발하는 경우 인터페이스를 사용하는 것입니다. 큰 기능 단위를 디자인하는 경우 추상 클래스를 사용하십시오.

이것이 어떤 사람들에게 도움이 되길 바랍니다!

또한 더 좋은 예를 생각하거나 무언가를 지적하고 싶다면 아래 의견에서 그렇게하십시오!


6

인터페이스를 통한 기본 클래스의 경우는 Submain .NET 코딩 지침에 잘 설명되어 있습니다.

기본 클래스와 인터페이스 인터페이스 유형은 값에 대한 부분 설명이며 많은 객체 유형에서 지원 될 수 있습니다. 가능하면 인터페이스 대신 기본 클래스를 사용하십시오. 버전 관리 측면에서 클래스는 인터페이스보다 유연합니다. 클래스를 사용하면 버전 1.0을 제공 한 다음 버전 2.0에서 클래스에 새 메소드를 추가 할 수 있습니다. 메소드가 추상적이지 않은 한 기존 파생 클래스는 계속 변경되지 않습니다.

인터페이스는 구현 상속을 지원하지 않기 때문에 클래스에 적용되는 패턴은 인터페이스에 적용되지 않습니다. 인터페이스에 메소드를 추가하는 것은 기본 클래스에 추상 메소드를 추가하는 것과 같습니다. 클래스가 새 메소드를 구현하지 않기 때문에 인터페이스를 구현하는 모든 클래스가 중단됩니다. 다음 상황에서는 인터페이스가 적합합니다.

  1. 관련이없는 여러 클래스가 프로토콜을 지원하려고합니다.
  2. 이러한 클래스는 이미 기본 클래스를 설정했습니다 (예 : 일부는 UI (사용자 인터페이스) 컨트롤이고 일부는 XML 웹 서비스)
  3. 집계는 적절하지 않거나 실용적이지 않습니다. 다른 모든 상황에서 클래스 상속은 더 나은 모델입니다.

이 답변에 더 많은 관심을 가져야한다고 생각합니다. 여기에는 많은 대답이 포함되어 있습니다. 나는 완전히 동의한다고 말하지는 않지만 여기에는 큰 요점이 있습니다.
kayleeFrye_onDeck

5

한 가지 중요한 차이점은 하나의 기본 클래스 만 상속 할 수 있지만 많은 인터페이스를 구현할 수 있다는 것 입니다. 따라서 다른 기본 클래스를 상속 할 필요가 없다고 확신 하는 경우에만 기본 클래스를 사용하려고합니다 . 또한 인터페이스가 커지면 인터페이스가 독립적 인 기능을 정의하는 몇 가지 논리적 부분으로 분류하기 시작해야합니다. 클래스가 모든 것을 구현할 수 없다는 규칙이 없기 때문에 인터페이스를 모두 상속하는 인터페이스).


4

객체 지향 프로그래밍에 대해 처음 배우기 시작했을 때, 상속을 사용하여 일반적인 행동을 공유하는 것은 쉽고 아마도 일반적인 실수였습니다. 그 행동이 객체의 특성에 필수적이지 않은 경우에도 마찬가지입니다.

이 특정 질문에 많이 사용 된 예를 더 발전시키기 위해 여자 친구, 자동차, 퍼지 담요 등 많은 애완 동물이있을 수 있습니다. 이것으로부터.

그러나 애완 동물을 기르는 것은 이러한 물건의 본질의 일부가 아닙니다. 성격에 필수적인 훨씬 더 중요한 개념이 있습니다 . 여자 친구는 사람이고, 자동차는 육상 차량이며, 고양이는 포유 동물입니다 ...

동작은 (클래스의 기본 인터페이스를 포함하여) 인터페이스에 먼저 할당되어야하고 (a) 더 큰 클래스의 하위 집합 인 큰 클래스 그룹에 공통적 인 경우에만 기본 클래스로 승격되어야합니다. "cat"및 "person"은 "mammal"의 하위 집합입니다.

캐치 (catch)는 처음에했던 것보다 객체 지향 디자인을 충분히 이해 한 후에는 일반적으로 생각하지 않고 자동으로 수행합니다. 따라서 "추상 클래스가 아닌 인터페이스에 대한 코드"라는 문장의 진실은 누구에게나 귀찮게 말하고 다른 의미를 읽으려고한다고 믿기가 힘들다는 점이 분명해집니다.

내가 추가 할 또 다른 것은 클래스가 순수하고 추상적 이며 상속되지 않은 비 상속 멤버 또는 메서드가 자식, 부모 또는 클라이언트에 노출되지 않은 경우 왜 클래스입니까? 경우에 따라 인터페이스로, 다른 경우에는 Null로 대체 될 수 있습니다.


순전히 추상 클래스는 메소드의 기본 동작을 제공 할 수 있습니다. 구체적인 클래스가 모두 다시 구현하기 위해 중복되는 공통 메소드를 공유 할 때 유용합니다.
Adam Hughes

4

추상 클래스보다 인터페이스 선호

[이미 언급 한 두 가지] 고려할 요점은 다음과 같습니다.

  • 클래스는 여러 인터페이스를 구현할 수 있으므로 인터페이스가 더 유연합니다. Java에는 다중 상속이 없으므로 추상 클래스를 사용하면 사용자가 다른 클래스 계층을 사용할 수 없습니다. 일반적으로 기본 구현 또는 상태가없는 경우 인터페이스를 선호하십시오. Java 콜렉션은 이에 대한 좋은 예를 제공합니다 (Map, Set 등).
  • 추상 클래스는 더 나은 순방향 호환성을 허용한다는 이점이 있습니다. 클라이언트가 인터페이스를 사용하면 변경할 수 없습니다. 이들이 추상 클래스를 사용하는 경우 기존 코드를 손상시키지 않고 동작을 추가 할 수 있습니다. 호환성이 우려되는 경우 추상 클래스 사용을 고려하십시오.
  • 기본 구현 또는 내부 상태가 있더라도 인터페이스 및 추상 구현을 고려하십시오 . 이를 통해 고객을 지원할 수 있지만 원하는 경우 여전히 더 큰 자유를 허용합니다 [1].
    물론, 주제는 다른 곳에서 길게 논의되었다 [2,3].

[1] 물론 더 많은 코드를 추가하지만 간결함이 주요 관심사라면 우선 Java를 피했을 것입니다!

[2] Joshua Java, 효과적인 Java, 항목 16-18.

[3] http://www.codeproject.com/KB/ar ...


3

일반적인 구현을 위해 추상 클래스를 사용하는 것에 대한 이전 의견은 분명히 표시됩니다. 아직 언급하지 않은 한 가지 이점은 인터페이스를 사용하면 단위 테스트를 위해 모의 객체를 훨씬 쉽게 구현할 수 있다는 것입니다. Jason Cohen이 설명한대로 IPet 및 PetBase를 정의하면 실제 데이터베이스의 오버 헤드없이 실제 데이터를 테스트 할 때까지 다른 데이터 조건을 쉽게 조롱 할 수 있습니다.


3

무슨 뜻인지 알지 않는 한 기본 클래스를 사용하지 마십시오. 적용되는 경우 사용하고 그렇지 않으면 인터페이스를 사용하십시오. 그러나 작은 인터페이스에 대한 답변에 유의하십시오.

공개 상속은 OOD에서 과도하게 사용되며 대부분의 개발자가 인식하거나 기꺼이 기대하는 것보다 훨씬 더 많이 표현합니다. 참고 항목 Liskov Substitutablity 원리

요컨대, A가 "a"인 경우, A는 노출되는 모든 방법에 대해 B보다 크지 않고 B보다 작게 전달합니다.


3

명심해야 할 또 다른 옵션은 "Has-a"관계를 사용하는 것입니다. "일명"또는 "구성"으로 구현됩니다. 때때로 이것은 "is-a"상속을 사용하는 것보다 더 체계적이고 유연하게 구조를 구성하는 방법입니다.

개와 고양이가 모두 애완 동물을 가지고 있다고 말하는 것은 논리적으로 의미가 없을 수도 있지만, 일반적인 다중 상속 함정은 피합니다.

public class Pet
{
    void Bathe();
    void Train(Trick t);
}

public class Dog
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

public class Cat
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

예,이 예는 코드 중복이 많고 이러한 방식으로 작업을 수행하는 데 우아함이 부족함을 보여줍니다. 그러나 이것은 개와 고양이가 애완 동물 클래스에서 분리되는 데 도움이되며 (개와 고양이는 애완 동물의 개인 구성원에게 접근 할 수 없다는 점에서) 개와 고양이가 다른 것에서 상속받을 수있는 여지를 남겨 둡니다. 아마도 Mammal 클래스 일 것입니다.

개인 액세스가 필요하지 않고 일반적인 애완 동물 참조 / 포인터를 사용하여 개와 고양이를 참조 할 필요가없는 경우 구성이 바람직합니다. 인터페이스는 일반적인 참조 기능을 제공하며 코드의 세부 정보를 줄이는 데 도움이 될 수 있지만 잘못 구성 될 경우 혼란을 줄 수 있습니다. 상속은 개인 회원 액세스가 필요할 때 유용하며,이를 사용하면 Dog 클래스와 Cat 클래스를 Pet 클래스에 강력하게 결합 할 수 있습니다.

상속, 구성 및 인터페이스 사이에는 항상 올바른 방법이 없으며 세 가지 옵션을 모두 조화롭게 사용하는 방법을 고려하는 데 도움이됩니다. 세 가지 중에서 상속은 일반적으로 가장 적게 사용해야하는 옵션입니다.


3

개념적으로 인터페이스 는 객체가 제공 할 메소드 세트를 공식적으로 그리고 반 공식적으로 정의하는 데 사용됩니다. 공식적으로는 일련의 메소드 이름 및 서명을 의미하며, 반 공식적으로는 해당 메소드와 관련된 사람이 읽을 수있는 문서를 의미합니다.

인터페이스는 API에 대한 설명 일뿐입니다 (결국 API 는 애플리케이션 프로그래밍 인터페이스를 나타냄). 구현을 포함 할 수 없으며 인터페이스를 사용하거나 실행할 수 없습니다. 그들은 당신이 객체와 상호 작용하는 방법의 계약을 명시 적으로 만듭니다.

클래스는 구현을 제공하며 하나 이상의 인터페이스를 구현한다고 선언 할 수 있습니다. 경우 클래스가 상속하기위한 것입니다,이 협약은 "기본"으로 클래스 이름 앞에 붙이는 것입니다.

구별이 기본 클래스추상 기본 클래스 (ABC)가. ABC는 인터페이스와 구현을 함께 혼합합니다. 컴퓨터 프로그래밍 외부의 개요는 "요약", 즉 "추상 == 인터페이스"를 의미합니다. 추상 기본 클래스는 다음 인터페이스뿐만 아니라 상속하도록되어있는, 빈 부분 또는 완전 구현을 모두 설명 할 수 있습니다.

사용시기에 대한 의견 인터페이스추상 기본 클래스를 비교할 때의 클래스 은 개발중인 언어와 개발중인 언어에 따라 크게 달라질 수 있습니다. 인터페이스는 종종 Java 또는 C #과 같은 정적으로 유형이 지정된 언어와 만 관련이 있지만 동적 타입 언어는 인터페이스추상 기본 클래스를 가질 수 있습니다 . 파이썬에서 예를 들어, 구별는 것을 선언하는 클래스 사이에 분명하게되어 구현 인터페이스를 , 그리고 인스턴스 인 객체, 클래스 , 그리고 말한다 제공 하는 인터페이스를. 동적 인 언어에서 같은 클래스 의 두 인스턴스 인 두 객체가 완전히 다른 인터페이스 를 제공한다고 선언 할 수 있습니다. 파이썬에서 이것은 객체 속성에 대해서만 가능하지만 메소드는 클래스의 모든 객체간에 공유 상태 입니다. 그러나 Ruby에서 객체는 인스턴스 별 메소드를 가질 수 있으므로 동일한 클래스 의 두 객체 사이 의 인터페이스 는 프로그래머가 원하는만큼 달라질 수 있습니다 (그러나 Ruby에는 인터페이스를 명시 적으로 선언하는 방법이 없습니다).

동적 언어에서 객체에 대한 인터페이스는 종종 객체를 조사하고 객체가 제공하는 메소드를 요청하여 암시 적으로 가정됩니다 ( 하거나 ( 도약하기 전에 ) 객체 에서 원하는 인터페이스 를 사용하려고 시도 하고 객체의 경우 예외를 포착 됩니다. 해당 인터페이스를 제공하지 않습니다 ( 권한보다 용서를 구하는 것이 더 쉽습니다 ). 이로 인해 두 인터페이스 가 동일한 메소드 이름을 갖지만 의미 상 다른 "가양 성"이 발생할 수 있습니다 . 그러나 코드의 가능한 모든 사용을 예상하기 위해 선불로 지정할 필요가 없으므로 코드가 더 유연하다는 단점이 있습니다.


2

요구 사항에 따라 다릅니다. IPet이 충분히 단순하다면, 그것을 구현하는 것을 선호합니다. 그렇지 않으면 PetBase가 많은 기능을 구현하면 복제하지 않으려는 것입니다.

기본 클래스를 구현의 단점에 대한 요구 사항입니다 override(나 new) 기존의 방법. 따라서 가상 메서드가 만들어 지므로 개체 인스턴스 사용 방법에주의해야합니다.

마지막으로 .NET의 단일 상속이 나를 죽입니다. 순진한 예 : 사용자 정의 컨트롤을 만들고 있다고 상속한다고 가정 해보십시오 UserControl. 그러나 이제는 상속도받지 못했습니다 PetBase. 이렇게하면 다음과 같이 재구성 할 수 있습니다.PetBase 대신 반원 합니다.


2

나는 보통 필요할 때까지 구현하지 않습니다. 유연성이 조금 더 높기 때문에 추상 클래스보다 인터페이스를 선호합니다. 상속 클래스 중 일부에 공통적 인 동작이 있으면 위로 이동하여 추상 기본 클래스를 만듭니다. 나는 본질적으로 동일한 목적을 서버에두기 때문에 둘 다의 필요성을 보지 못합니다. 둘 다 갖는 것은 솔루션이 오버 엔지니어링 된 나쁜 코드 냄새 (imho)입니다.


2

C #과 관련하여 일부 의미에서 인터페이스와 추상 클래스는 서로 호환 될 수 있습니다. 그러나 차이점은 다음과 같습니다. i) 인터페이스는 코드를 구현할 수 없습니다. ii)이 때문에 인터페이스는 서브 클래스로 스택을 추가로 호출 할 수 없습니다. iii) 클래스에서 추상 클래스 만 상속 될 수있는 반면, 클래스에서 여러 인터페이스가 구현 될 수 있습니다.


2

def에 의해 interface는 다른 코드와 통신하기위한 계층을 제공합니다. 클래스의 모든 공용 속성 및 메서드는 기본적으로 암시 적 인터페이스를 구현합니다. 또한 어떤 클래스가 그 역할을 수행해야 할 때마다 인터페이스를 역할로 정의 할 수 있으며, 구현하는 클래스에 따라 다른 형태의 구현을 제공해야합니다. 따라서 인터페이스에 대해 이야기 할 때는 다형성에 대해 이야기하고 기본 클래스에 대해 이야기 할 때 상속에 대해 이야기하는 것입니다. 죄송합니다 두 가지 개념!


2

Interface> Abstract> Concrete 패턴이 다음 사용 사례에서 작동한다는 것을 알았습니다.

1.  You have a general interface (eg IPet)
2.  You have a implementation that is less general (eg Mammal)
3.  You have many concrete members (eg Cat, Dog, Ape)

추상 클래스는 구체적 클래스의 기본 공유 속성을 정의하지만 인터페이스를 적용합니다. 예를 들면 다음과 같습니다.

public interface IPet{

    public boolean hasHair();

    public boolean walksUprights();

    public boolean hasNipples();
}

이제 모든 포유류에 머리카락과 젖꼭지가 있기 때문에 (AFAIK, 나는 동물학자가 아닙니다) 이것을 추상 기본 클래스로 롤백 할 수 있습니다

public abstract class Mammal() implements IPet{

     @override
     public walksUpright(){
         throw new NotSupportedException("Walks Upright not implemented");
     }

     @override
     public hasNipples(){return true}

     @override
     public hasHair(){return true}

그리고 구체적인 수업은 단지 그들이 똑바로 걷도록 정의합니다.

public class Ape extends Mammal(){

    @override
    public walksUpright(return true)
}

public class Catextends Mammal(){

    @override
    public walksUpright(return false)
}

이 디자인은 구체적인 클래스가 많을 때 좋으며 인터페이스에 프로그래밍하기 위해 상용구를 유지하고 싶지 않습니다. 새 메소드가 인터페이스에 추가 된 경우 모든 결과 클래스가 손상되므로 인터페이스 방식의 장점을 여전히 얻을 수 있습니다.

이 경우 초록은 구체적 일 수도 있습니다. 그러나 추상적 지정은이 패턴이 채택되고 있음을 강조하는 데 도움이됩니다.


1

기본 클래스의 상속자는 "is a"관계를 가져야합니다. 인터페이스는 "구현"관계를 나타냅니다. 따라서 상속자가 관계를 유지할 때 기본 클래스 만 사용하십시오.


1

인터페이스를 사용하여 관련없는 클래스의 계약 ACROSS 제품군을 시행하십시오. 예를 들어 컬렉션을 나타내는 클래스에 대한 공통 액세스 방법이 있지만 완전히 다른 데이터를 포함 할 수 있습니다. 즉, 한 클래스는 쿼리의 결과 집합을 나타내고 다른 클래스는 갤러리의 이미지를 나타낼 수 있습니다. 또한 여러 인터페이스를 구현할 수 있으므로 클래스의 기능을 혼합 (및 의미) 할 수 있습니다.

클래스가 공통의 관계를 맺고 따라서 유사한 구조적 및 행동 적 서명이있을 때 상속을 사용하십시오. 즉, Car, Motorbike, Truck 및 SUV는 모든 바퀴, 최고 속도를 포함 할 수있는 모든 유형의 도로 차량입니다.

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