답변:
Gosling은 extends 키워드를 사용하지 말라고 말하지 않았습니다 ... 코드 재사용을위한 수단으로 상속을 사용하지 말라고했습니다.
상속 자체는 나쁘지 않으며 OO 구조를 만드는 데 사용할 수있는 매우 강력한 도구입니다. 그러나 올바르게 사용되지 않으면 (즉, 객체 구조를 만드는 것 이외의 용도로 사용되는 경우 ) 매우 밀접하게 결합되어 유지 관리하기 어려운 코드를 만듭니다.
상속은 다형성 구조를 만드는 데 사용해야하므로 인터페이스와 마찬가지로 쉽게 수행 할 수 있으므로 객체 구조를 디자인 할 때 클래스가 아닌 인터페이스를 사용해야합니다. 확장을 사용하여 한 객체에서 다른 객체로 코드를 복사하여 붙여 넣는 것을 피하는 방법을 발견하면 잘못 사용하고 특수 클래스를 사용 하여이 기능을 처리하고 대신 컴포지션을 통해 해당 코드를 공유하는 것이 좋습니다.
Liskov 대체 원칙 에 대해 읽고 이해하십시오. 클래스 (또는 그 문제에 대한 인터페이스)를 확장하려고 할 때마다 원칙을 위반하는지 스스로에게 물어보십시오. 그렇다면 자연스럽게 상속을 사용하는 것입니다. 수행하기 쉽고 종종 올바른 것처럼 보이지만 코드를 유지 관리, 테스트 및 진화하기가 더 어려워집니다.
객체 지향 프로그래밍 초기에 구현 상속은 코드 재사용의 황금 망치였습니다. 어떤 클래스에 필요한 기능이 있다면, 그 클래스에서 공개적으로 상속하는 것이 었습니다 (이것은 C ++이 Java보다 널리 보급 된 시절이었습니다). 그리고 그 기능은 "무료"입니다.
제외하고, 그것은 실제로 무료가 아니었다. 그 결과 처리하기 어려운 다이아몬드 모양의 상속 트리를 포함하여 이해하기 거의 불가능한 매우 복잡한 상속 계층 구조가 만들어졌습니다. 이것은 Java 설계자가 클래스의 다중 상속을 지원하지 않기로 선택한 이유의 일부입니다.
Gosling의 조언 (및 기타 진술)은 상당히 심각한 질병에 대한 강력한 약이며 일부 아기는 목욕물로 버릴 수 있습니다. 상속을 사용하여 진정으로 클래스 간의 관계를 모델링하는 데 아무런 문제가 없습니다 is-a
. 그러나 다른 클래스의 기능을 사용하려는 경우 다른 옵션을 먼저 조사하는 것이 좋습니다.
상속은 최근 상당히 무서운 평판을 얻었습니다. 이것을 피해야하는 한 가지 이유는 "상속 상속 구성"의 디자인 원칙과 관련이 있는데, 이는 기본적으로 사람들이 진정한 관계가 아닌 곳에서 상속을 사용하지 말고 코드 작성을 절약하도록 권장합니다. 이러한 종류의 상속은 버그를 찾기 어렵고 수정하기가 어려울 수 있습니다. 친구가 대신 인터페이스를 사용하도록 제안한 이유는 인터페이스가 분산 된 API에 대해보다 유연하고 유지 관리 가능한 솔루션을 제공하기 때문입니다. 클래스를 노출하면 사용자 코드가 손상되는 경우가 많으므로 사용자 코드를 손상시키지 않고 API를 변경할 수 있습니다. .Net에서 가장 좋은 예는 List와 IList의 사용 차이입니다.
하나의 클래스 만 확장 할 수 있기 때문에 많은 인터페이스를 구현할 수 있습니다.
상속은 당신이 무엇인지 정의한다고 들었지만, 인터페이스는 당신이 할 수있는 역할을 정의합니다.
인터페이스는 또한 다형성을 더 잘 사용할 수있게합니다.
그것은 이유의 일부일 수 있습니다.
나는 이것도 가르쳐 왔으며 가능한 경우 인터페이스를 선호합니다 (물론 여전히 이해되는 상속을 사용합니다).
내가 생각하는 것 중 하나는 코드를 특정 구현에서 분리하는 것입니다. ConsoleWriter라는 클래스가 있는데 무언가를 작성하는 메소드로 전달되어 콘솔에 씁니다. 이제 GUI 창으로 인쇄로 전환하고 싶다고 가정 해 봅시다. 이제 메서드를 수정하거나 GUIWriter를 매개 변수로 사용하는 새 메서드를 작성해야합니다. IWriter 인터페이스를 정의하여 시작하고 메소드를 IWriter로 가져간 경우 ConsoleWriter (IWriter 인터페이스를 구현할 것)로 시작한 다음 나중에 GUIWriter (IWriter 인터페이스를 구현 함)라는 새 클래스를 작성할 수 있습니다. 전달되는 클래스를 전환해야합니다.
또 다른 것은 (C #에서는 사실이지만 Java에 대해서는 확실하지 않음) 1 클래스 만 확장 할 수 있지만 많은 인터페이스를 구현할 수 있습니다. Teacher, MathTeacher 및 HistoryTeacher라는 클래스가 있다고 가정하겠습니다. 이제 MathTeacher와 HistoryTeacher는 Teacher에서 확장되지만 MathTeacher와 HistoryTeacher 인 사람을 나타내는 클래스를 원한다면 어떻게해야합니까? 한 번에 하나씩 만 수행 할 수있을 때 여러 클래스에서 상속하려고하면 꽤 지저분해질 수 있습니다 (방법이 있지만 정확하게 최적은 아닙니다). 인터페이스를 사용하면 IMathTeacher 및 IHistoryTeacher라는 2 개의 인터페이스를 가질 수 있으며 Teacher에서 확장하여 2 개의 인터페이스를 구현하는 하나의 클래스를 가질 수 있습니다.
인터페이스를 사용하는 한 가지 단점은 때때로 사람들이 코드를 복제하는 것을 볼 수 있다는 것입니다 (인터페이스를 구현하는 각 클래스에 대한 구현을 작성해야하기 때문에)이 문제에 대한 명확한 해결책이 있습니다 (예 : 델리게이트와 같은 것) Java와 동등한 것).
상속보다 인터페이스를 사용하는 가장 큰 이유는 구현 코드의 분리라고 생각하지만 상속이 여전히 매우 유용하기 때문에 상속이 악하다고 생각하지 않습니다.
클래스에서 상속받은 경우 해당 클래스에 대한 종속성뿐만 아니라 해당 클래스의 클라이언트가 작성한 암시 적 계약도 존중해야합니다. Bob 아저씨 는 기본 사각형 확장 사각형 딜레마를 사용하여 Liskov 대체 원칙 을 미묘하게 위반하는 방법에 대한 훌륭한 예를 제공합니다 . 인터페이스는 구현이 없기 때문에 숨겨진 계약도 없습니다.