“상속보다 선호되는 구성”이라는이 개념은 어디에서 왔습니까?


143

지난 몇 달 동안, "상속에 대한 선호 구성"이라는 진언은 아무데도 생겨나 고 프로그래밍 커뮤니티 내에서 거의 일종의 밈이 된 것 같습니다. 그리고 그것을 볼 때마다 조금 미스터리합니다. 누군가가 "해머보다 드릴을 선호한다"고 말한 것과 같습니다. 내 경험상 구성과 상속은 서로 다른 유스 케이스를 가진 두 가지 도구이며, 상호 교환 가능하고 하나가 본질적으로 다른 것보다 우월한 것으로 간주하는 것은 의미가 없습니다.

또한 상속이 나쁘고 구성이 좋은지에 대한 실제 설명을 결코 보지 못 하므로 더 의심 스럽습니다. 믿음으로 받아 들여 져야합니까? Liskov 대체 및 다형성은 잘 알려져 있고 명확한 이점이 있으며 IMO는 객체 지향 프로그래밍을 사용하는 모든 요점을 구성하며 아무도 왜 구성을 위해 폐기해야하는지 설명하지 않습니다.

누구나이 개념의 출처와 그 개념의 근거를 알고 있습니까?


45
다른 사람들이 지적했듯이 오랜 시간이 지났습니다. 지금 막 듣고 있다는 사실에 놀랐습니다. Java와 같은 언어로 큰 시스템을 구축해 온 사람이라면 누구나 직관적입니다. 면접의 핵심이며 후보자가 상속에 대해 이야기하기 시작할 때 그들의 기술 수준과 경험의 양을 의심하기 시작합니다. 상속이 취성 솔루션 인 이유는 다음과 같습니다. artima.com/lejava/articles/designprinciples4.html
Janx

6
@Janx : 어쩌면 그게 다야. Java와 같은 언어로 큰 시스템을 구축하지 않습니다. 나는 델파이에서 그것들을 만들고, Liskov 대체와 다형성이 없다면 우리는 아무것도하지 않을 것입니다. 객체 모델은 Java 또는 C ++과는 특정 방식이 다르며, 델파이에는이 최대 값이 해결하려는 것으로 보이는 많은 문제가 실제로 존재하지 않거나 훨씬 덜 문제가됩니다. 다른 관점에서 다른 관점, 나는 추측한다.
메이슨 휠러

14
나는 델파이에서 비교적 큰 시스템을 구축하는 팀에서 몇 년을 보냈고 키 큰 상속 트리는 확실히 우리 팀을 물고 심각한 고통을 초래했습니다. SOLID 원칙에주의를 기울이면 Delphi를 사용하지 않고 문제 영역을 피할 수 있습니다.
Bevan

8
지난 몇 달?!?
Jas

6
IMHO의 개념은 인터페이스 상속 (순수한 인터페이스를 사용한 서브 타이핑)과 구현 상속을 모두 지원하는 다양한 언어에 완전히 적용되지 않았습니다. 너무 많은 사람들이이 진언을 따르고 충분한 인터페이스를 사용하지 않습니다.
Uri

답변:


136

GoF 이전의 구성 대 상속 상속 토론을 들어 본 것 같지만 특정 소스에 손가락을 넣을 수는 없습니다. 어쨌든 Booch 일 수도 있습니다.

<랜트>

아 그러나 많은 진언과 마찬가지로, 이것은 전형적인 선을 따라 퇴화되었습니다.

  1. 그것은 원래의 복잡한 토론을 상기 시키기 위해 캐치 프레이즈 를 만들어내는 존경받는 출처의 상세한 설명과 논증으로 소개됩니다.
  2. 그것은 일반적으로 n00b 실수에 대해 언급 할 때, 몇 시간 동안 알고있는 클럽에 의해 알고있는 클럽의 윙크와 공유됩니다.
  3. 곧 설명을 읽지 않는 수천명의 사람들이 머지 않아 반복적으로 생각하지만 그것을 생각하지 않는 변명으로 사용하고 다른 사람들보다 우월하고 값 싸게 느끼는 방법으로 사용하는 것을 좋아합니다.
  4. 결국, 어느 정도의 합리적인 디버 킹이 "meme"조류를 막을 수 없으며, 패러다임은 종교와 교리로 변질된다.

원래 n00bs를 깨달음으로 이끌려는 밈은 이제 무의식적으로 그들을 곤경 화하는 클럽으로 사용됩니다.

구성과 상속은 매우 다르며 서로 혼동해서는 안됩니다. 구성 이 많은 추가 작업으로 상속을 시뮬레이트하는 데 사용될 수는 있지만 , 이것이 상속을 2 등 시민으로 만들거나 구성을 좋아하는 아들로 만들지는 않습니다. 많은 n00bs가 상속을 지름길로 사용하려고한다고해서 메커니즘이 무효화되지는 않으며, 거의 모든 n00b가 실수를 통해 학습하여 개선합니다.

제발 THINK 당신의 디자인에 대해, 그리고 구호를 내뿜는 중지합니다.

</ rant>


16
Booch는 구현 상속이 클래스간에 높은 결합을 유발한다고 주장합니다. 반면에 그는 상속을 절차 적 프로그래밍과 OOP를 구별하는 핵심 개념으로 간주합니다.
Nemanja Trifunovic

13
이런 종류의 단어가 있어야합니다. 조기 역류 ?
Jörg W Mittag

8
@ Jörg : 조기 역류와 같은 용어에 어떤 일이 일어날 지 알고 있습니까? 위에서 설명한 내용이 정확합니다. :) (Btw. 언제 역류가 조숙하지 않은가?)
Bjarke Freund-Hansen

6
@Nemanja : 그렇습니다. 문제는이 커플 링이 실제로 나쁜지 여부입니다. 클래스가 개념적으로 강력하게 결합되어 개념적으로 수퍼 타입-하위 유형 관계를 형성하고 언어 수준에서 공식적으로 분리되어 있어도 실제로 분리 될 수없는 경우에는 강한 결합이 좋습니다.
dsimcha

5
만트라는 간단합니다. "만약 당신이 어떤 모양으로 보이도록 모양을 만들 수 있다고해서 모든 것을 재생 모양으로 만들어야한다는 의미는 아닙니다." ;)
Evan Plaice

73

경험.

당신이 말했듯이, 그들은 다른 직업을위한 도구이지만 사람들이 그런 식으로 그것을 사용하지 않았기 때문에 문구가 나왔습니다.

상속은 주로 다형성 도구이지만 나중에 위험에 처한 사람들은 코드를 재사용 / 공유하는 방법으로 사용하려고합니다. 이론적 근거는 "내가 상속하면 모든 방법을 무료로 얻는다"는 것이지만이 두 클래스가 잠재적으로 다형성 관계가 없다는 사실을 무시합니다.

따라서 상속보다 구성을 선호하는 이유는 무엇입니까? 클래스 간의 관계가 다형성이 아닌 경우가 많기 때문입니다. 사람들이 상속함으로써 무릎을 꿇지 말라고 반응을 상기시키는 데 도움이됩니다.


7
따라서 기본적으로 "Liskov 대체를 이해하지 못하면 OOP를 사용해서는 안됩니다"라고 말할 수 없으므로 무능한 사람이 입는 피해를 제한하려는 시도 대신 "상속 상속 선호 구성"이라고합니다. 코더?
메이슨 휠러

13
@Mason : 어떤 진언과 마찬가지로 "상속보다 선호하는 구성"은 초보자 프로그래머를 대상으로합니다. 상속을 언제 사용할지, 언제 컴포지션을 사용할지를 알고 있다면, 그런 만트라를 반복하는 것은 의미가 없습니다.
Dean Harding

7
@Dean- 초보자 가 상속 우수 사례를 이해하지 못하는 사람과 같은지 잘 모르겠습니다 . 나는 그것보다 더 많은 것이 있다고 생각합니다. 불쌍한 상속은 직장에서 많은 두통의 원인이며 "초보자"로 간주되는 프로그래머가 작성한 코드는 아닙니다.
Nicole

6
@Renesis : "어떤 사람들은 10 년의 경험이 있고 어떤 사람들은 1 년의 경험이 10 번 반복됩니다"라고 들었습니다.
메이슨 휠러

8
처음 OO를 받고 나서 상당히 큰 프로젝트를 디자인하고 있었고 크게 의존했습니다. 잘 작동했지만, 여기저기서 약간의 자극을 유발하고 때로는 특정 리 팩터를 완전한 암캐로 만드는 취성 디자인을 끊임없이 발견했습니다. 전체 경험을 설명하기는 다소 어렵지만 "상속적인 선호보다 선호"라는 문구가이를 정확하게 설명합니다. 그것은 "상속을 피하라"고 말하거나 암시하지도 않으며, 선택이 명확하지 않을 때 약간의 멍청함을줍니다.
Bill K

43

새로운 아이디어는 아니지만 1994 년에 출판 된 GoF 디자인 패턴 책에서 실제로 소개되었다고 생각합니다.

상속의 주요 문제점은 화이트 박스라는 것입니다. 정의에 따라 상속받은 클래스의 구현 세부 사항을 알아야합니다. 반면에 작문을 사용하면 작곡하는 수업의 공개 인터페이스에만 관심이 있습니다.

GoF 책에서 :

상속은 부모의 구현에 대한 세부 사항에 서브 클래스를 노출시킵니다. 종종 상속은 캡슐화를 깨뜨립니다

GoF 책에있는 wikipedia 기사에는 괜찮은 소개가 있습니다.


14
나는 그것에 동의하지 않습니다. 상속받은 클래스의 구현 세부 사항을 알 필요는 없습니다. 수업에 노출 된 공개보호 회원 만 구현 세부 사항을 알아야 할 경우 기본 클래스를 작성한 사람이나 누군가가 잘못된 일을하고 있고 결함이 기본 클래스에 있으면 컴포지션을 사용하여 문제를 해결하거나 해결할 수 없습니다.
메이슨 휠러

18
읽지 않은 것에 어떻게 동의하지 않습니까? GoF의 탄탄한 페이지와 반의 토론이 있습니다.
철학자

7
@ pholosodad : 나는 읽지 않은 것에 동의하지 않습니다. 나는 Dean이 썼던 내용에 동의하지 않는다. "정의상, 당신은 상속받은 클래스의 구현 세부 사항을 알아야한다"고 읽었다.
메이슨 휠러

10
내가 쓴 것은 GoF 책에 설명 된 내용의 요약 일뿐입니다. 나는 그것을 조금 강하게 말했을 수도 있지만 ( 모든 구현 세부 사항 을 알 필요는 없습니다 ) GoF가 상속보다 구성을 선호한다고 말하는 일반적인 이유입니다.
Dean Harding

2
내가 틀렸다면 정정하십시오. "암시 적 (즉, 상속)보다 명시 적 클래스 관계 (즉, 인터페이스) 선호). 전자는 필요한 방법을 알려주지 않고 필요한 것을 알려줍니다. 후자는 그것을하는 방법을 알려줄뿐만 아니라 길을 후회하게 만듭니다.
Evan Plaice


23

"상속에 대한 구성"은 "클래스의 데이터 (또는 동작)가 다른 클래스에 통합되어야한다고 생각할 때 상속을 맹목적으로 적용하기 전에 항상 컴포지션 사용을 고려하십시오"라고 말하는 짧은 (그리고 명백하게 오도하는) 방법입니다.

왜 이것이 사실입니까? 상속은 두 클래스 사이에 긴밀한 컴파일 타임 커플 링을 생성합니다. 대조적으로 컴포지션은 느슨하게 커플 링되며, 무엇보다도 문제를 명확하게 분리 할 수 ​​있으며 런타임에 종속성을 전환 할 수 있으며 더 쉽고 고립 된 종속성 테스트 가능성이 있습니다.

그것은 상속이 유용하지 않다는 것이 아니라 비용이 들기 때문에주의해서 다루어야한다는 것을 의미합니다. 실제로 "상속 상속 구성"은 종종 구성 종속이 구체적인 서브 클래스 자체가 아닌 추상 수퍼 클래스가되기를 원하기 때문에 종종 "구성 + 상속 상속"이됩니다. 런타임에 종속성의 다른 구체적인 구현간에 전환 할 수 있습니다.

이런 이유로 (바람직하게는) 상속이 바닐라 상속보다 인터페이스 구현 또는 추상 클래스의 형태로 더 자주 사용되는 것을 볼 수 있습니다.

비유적인 예는 다음과 같습니다.

"스네이크 클래스가 있는데 스네이크가 물릴 때 발생하는 일을 그 클래스의 일부로 포함하고 싶습니다. 스네이크가 Bite () 메소드가있는 BiterAnimal 클래스를 상속하고 그 바이트를 무시하여 독한 물기를 반영하도록 유혹하고 싶습니다 그러나 컴포지션에 대한 컴포지션은 컴포지션 대신 컴포지션을 사용해야한다고 경고합니다 ... 제 경우에는 Bite 멤버가있는 Snake로 변환 될 수 있습니다 .Bite 클래스는 여러 서브 클래스가있는 추상 (또는 인터페이스) 일 수 있습니다. VenomousBite 및 DryBite 서브 클래스를 사용하고 뱀이 자라면서 같은 뱀 인스턴스에서 물기를 바꿀 수 있다는 것과 같은 좋은 것들이 있습니다. 또한 별도의 클래스에서 Bite의 모든 효과를 처리하면 Frost 클래스에서 재사용 할 수 있습니다 , 서리가 물지 만 BiterAnimal이 아니기 때문에 ... "


4
뱀에 대한 아주 좋은 예입니다. 나는 최근에 내 자신의 수업과 비슷한 사건을 만났다
Maksee

22

구성에 대한 몇 가지 가능한 주장 :

컴포지션은 언어 / 프레임 워크와 무관하게 조금씩
상속되며 서브 / 수퍼 클래스가 액세스 할 수있는 기능과 가상 메소드에 대한 성능 관련 측면에서 언어마다 언어마다 다릅니다. 컴포지션은 매우 기본적이고 요구됩니다. 언어 지원이 거의 없으므로 다른 플랫폼 / 프레임 워크에 걸친 구현은 컴포지션 패턴을보다 쉽게 ​​공유 할 수 있습니다.

컴포지션은 객체를 만드는 매우 간단하고 촉각적인 방법입니다
상속은 비교적 이해하기 쉽지만 실제 생활에서는 쉽게 설명 할 수 없습니다. 실생활의 많은 물체는 여러 부분으로 나누어 져 구성 될 수 있습니다. 자전거는 두 바퀴, 프레임, 시트, 체인 등을 사용하여 만들 수 있다고 가정하십시오. 구성으로 쉽게 설명 할 수 있습니다. 상속 은유에서 자전거는 외발 자전거를 확장 할 수 있지만 구성보다 실제 그림에서 훨씬 더 멀리 있습니다 (분명히 상속 사례는 아니지만 요점은 동일합니다). 상속이라는 단어 (적어도 대부분의 미국 영어 사용자가 기대하는 것)조차도 소프트웨어에서 그 의미와 약간의 상관 관계가 있지만 "느슨한 친척에게서 넘어간 것"이라는 선을 따라 의미를 자동으로 불러옵니다.

컴포지션은 거의 항상 유연합니다
컴포지션을 사용하면 항상 자신의 동작을 정의하거나 구성된 파트의 해당 부분을 노출하도록 선택할 수 있습니다. 이렇게하면 상속 계층 (가상 대 비가 상 등)에 의해 부과 될 수있는 제한에 직면하지 않습니다.

따라서 컴포지션은 상속보다 이론적 인 제약이 적은 더 간단한 은유이기 때문일 수 있습니다. 또한 이러한 특정 이유는 디자인 타임에 더 분명하거나 상속의 일부 문제를 처리 할 때 발생할 수 있습니다.

면책 조항 :
분명히이 명확한 절단 / 일방 통행 거리는 아닙니다. 각 디자인은 여러 패턴 / 툴의 평가를 장점으로합니다. 상속은 널리 사용되며 많은 이점이 있으며 여러 번 구성보다 더 우아합니다. 이것들은 작곡을 선호 할 때 사용할 수있는 몇 가지 이유 일뿐입니다.


1
"분명히이 명확한 컷 / 일방 통행 거리가 아닙니다." 구성이 상속보다 더 유연하지 않은 경우는 언제입니까? 나는 그것을 아주 많이 있다는 주장 이다 일방 통행로. 상속은 특별한 경우의 구성에 대한 구문 설탕입니다.
weberc2

조성물은 종종 하지 하여 조성물을 구현하는 언어 사용시가요 명백하게 구현 이러한 인터페이스는 시간이 지남에 역방향 호환 방식으로 진화를 허용하지 않기 때문에, 인터페이스. #jmtcw
MikeSchinkel

11

아마도 당신은 사람들이 지난 몇 개월 동안 이것을 말한 것을 보았지만, 그보다 훨씬 더 좋은 프로그래머에게 알려져 있습니다. 나는 약 10 년 동안 적절한 곳에서 분명히 말하고 있습니다.

개념의 요점은 상속에 대한 개념적 오버 헤드가 크다는 것입니다. 상속을 사용할 때 모든 단일 메소드 호출에는 암시 적 디스패치가 있습니다. 깊은 상속 트리 또는 다중 디스패치가 있거나 (또는 ​​더 나쁜 경우) 둘 다있는 경우 특정 호출에서 특정 메소드가 어디로 디스패치되는지 파악하면 왕실 PITA가 될 수 있습니다. 코드에 대한 올바른 추론을 더 복잡하게 만들고 디버깅을 더 어렵게 만듭니다.

간단한 예를 들어 설명하겠습니다. 상속 트리에서 누군가 이름을 method라고하자 foo. 그런 다음 다른 사람이 와서 foo나무 꼭대기에 추가 하지만 다른 일을합니다. (이 경우는 다중 상속에서 더 흔합니다.) 이제 루트 클래스에서 일하는 사람이 모호한 하위 클래스를 깨뜨 렸고 아마도이를 인식하지 못했을 것입니다. 단위 테스트를 100 % 적용 할 수 있으며 최상위에있는 사람은 하위 클래스 테스트를 생각하지 않으며 하위 클래스 테스트는 상위에서 작성된 새 메소드를 테스트하지 않기 때문에이 파손을 알 수 없습니다. . (이를 잡아주는 단위 테스트를 작성하는 방법이 있지만, 그렇게 쉽게 테스트를 작성할 수없는 경우도 있습니다.)

반대로 컴포지션을 사용할 때 각 통화마다 통화를 전달하는 것이 더 명확합니다. (예를 들어, 의존성 주입과 같은 제어 역전을 사용하는 경우 호출이 진행되는 위치를 파악하는 것도 문제가 될 수 있지만 일반적으로 알아내는 것이 더 간단합니다.) 이렇게하면 추론이 쉬워집니다. 보너스로, 컴포지션은 메소드를 서로 분리시킵니다. 위의 예제는 자식 클래스가 모호한 구성 요소로 이동하기 때문에 발생하지 않아야하며 호출 foo이 모호한 구성 요소 또는 주 개체에 대한 것인지 여부에 대한 질문은 없습니다 .

상속과 구성은 서로 다른 두 가지 유형의 서비스를 제공하는 두 개의 매우 다른 도구라는 것이 옳습니다. 물론 상속은 개념적 오버 헤드를 가져 오지만 그것이 작업에 적합한 도구 인 경우에는 사용하지 않고 손으로하는 일보다 개념적 오버 헤드가 줄어 듭니다. 그들이하는 일을 아는 사람은 결코 상속을 사용해서는 안된다고 말하지 않을 것입니다. 그러나 그것이 옳은 일인지 확인하십시오.

불행히도 많은 개발자는 객체 지향 소프트웨어에 대해 배우고 상속에 대해 배우고 가능한 한 자주 새로운 도끼를 사용합니다. 즉, 컴포지션이 올바른 도구 인 상속을 사용하려고합니다. 그들은 시간이 지남에 따라 더 잘 배우기를 희망하지만, 사지 등을 몇 개 제거한 후에야 종종 이런 일이 발생하지 않습니다. 나쁜 생각이라고 미리 말하는 것은 학습 과정의 속도를 높이고 부상을 줄이는 경향이 있습니다.


3
좋아, 나는 C ++ 관점에서 의미가 있다고 생각한다. 델파이의 문제가 아니기 때문에 제가 생각하지 않은 것입니다. (여러 상속이 없으며 기본 클래스에 메서드가 있고 기본 메서드를 재정의하지 않는 파생 클래스에 동일한 이름의 다른 메서드가 있으면 컴파일러에서 경고를 표시하므로 실수로 종료되지 않습니다. – 이런 종류의 문제가 있습니다.)
Mason Wheeler

1
@Mason : 취약한 기본 클래스 상속 문제와 관련하여 Ander의 Object Pascal (일명 Delphi) 버전은 C ++ 및 Java보다 우수합니다. C ++ 및 Java와 달리 상속 된 가상 메서드의 오버로드는 암시 적이 지 않습니다.
bit-twiddler

2
@ bit-twiddler, C ++ 및 Java에 대해 말하는 것은 Smalltalk, Perl, Python, Ruby, JavaScript, Common Lisp, Objective-C 및 모든 형태의 OO 지원을 제공하는 다른 모든 언어에 대해 말할 수 있습니다. 인터넷 검색은 C #이 Object Pascal의 리드를 따르는 것으로 제안합니다.
btilly

3
Anders Hejlsberg는 볼랜드의 Object Pascal (일명 Delphi) 및 C #의 풍미를 디자인했기 때문입니다.
비트 트위 들러

8

OO 초보자는 필요하지 않을 때 상속을 사용하는 경향이 있다는 관찰에 대한 반응입니다. 상속은 확실히 나쁜 것은 아니지만 과용 될 수 있습니다. 한 클래스에서 다른 클래스의 기능 만 필요한 경우 컴포지션이 작동합니다. 다른 상황에서는 상속이 작동하고 구성이 작동하지 않습니다.

클래스에서 상속하는 것은 많은 것을 의미합니다. 그것은 파생물 이 기본 유형의 유형이라는 것을 암시합니다 ( gory 세부 사항 은 Liskov 대체 원칙 참조). 파생 된 액세스 권한을베이스의 멤버 및 멤버에 액세스 할 수 있습니다. 밀접한 관계입니다. 즉, 커플 링이 높으며 하나를 변경하면 다른 것을 변경해야 할 가능성이 높습니다.

커플 링은 나쁜 것입니다. 프로그램을 이해하고 수정하기가 어렵습니다. 다른 것들은 동일하기 때문에 항상 커플 링이 적은 옵션을 선택해야합니다.

따라서 컴포지션 또는 상속이 효과적으로 작업을 수행하는 경우 컴포지션이 낮으므로 컴포지션을 선택하십시오. 구성이 효과적으로 작업을 수행하지 않고 상속이 수행되면 상속을 선택해야합니다.


"다른 상황에서는 상속이 작동하고 구성이되지 않습니다." 언제? 상속은 컴포지션과 인터페이스 다형성을 사용하여 모델링 할 수 있으므로 컴포지션이 작동하지 않는 환경이 있으면 놀랍습니다.
weberc2

8

여기에 내 두 센트가 있습니다 (이미 제기 된 모든 훌륭한 포인트를 넘어서).

IMHO, 대부분의 프로그래머는 실제로이 개념을 가르치는 방식의 결과로 상속을받지 않고 과도하게 사용하지 않는다는 사실에 달려 있습니다. 이 개념은 올바른 방법을 가르치는 데 초점을 맞추지 않고 과용하지 않도록 설득하는 방법으로 존재합니다.

가르치거나 멘토링하는 데 시간을 보낸 사람은 이것이 특히 패러다임에 경험이있는 새로운 개발자에게 자주 발생한다는 것을 알고 있습니다.

이 개발자들은 처음에는 상속이이 무서운 개념이라고 생각합니다. 따라서 인터페이스 겹침 (예 : 공통 하위 유형 지정없이 동일한 지정된 동작)과 공통 기능을 구현하기위한 전역으로 유형을 작성하게됩니다.

그런 다음 (종종 지나친 가르침의 결과로) 상속의 이점에 대해 배우지 만 재사용 할 수있는 포괄적 인 해결책으로 종종 가르쳐집니다. 그들은 공유 된 행동이 상속을 통해 공유되어야한다는 인식으로 끝납니다. 이는 서브 타이핑보다는 구현 상속에 초점을 맞추기 때문입니다.

80 % 정도면 충분합니다. 그러나 다른 20 %는 문제가 시작되는 곳입니다. 재 작성을 피하고 구현 공유를 이용했는지 확인하기 위해 기본 추상화가 아닌 의도 된 구현을 중심으로 계층 구조를 설계하기 시작합니다. "스택이 이중 연결 목록에서 상 속됨"이 이에 대한 좋은 예입니다.

대부분의 교사들은이 시점에서 인터페이스 개념을 도입 할 것을 고집하지 않습니다. 왜냐하면 다른 개념이거나 (C ++에서는)이 단계에서 가르치지 않은 추상 클래스와 다중 상속을 사용하여 가짜로 만들어야하기 때문입니다. 자바에서 많은 교사들은 "다중 상속 금지"또는 "다중 상속이 악하다"는 인터페이스의 중요성을 구별하지 않습니다.

이 모든 것은 구현 상속으로 불필요한 코드를 작성하지 않아도되는 수많은 아름다움을 배웠고, 수많은 직관 위임 코드가 부 자연스럽게 보입니다. 따라서 컴포지션은 지저분 해 보이므로 새로운 프로그래머가 어쨌든 사용하도록 강요하려면 이러한 규칙을 사용해야합니다.


이것이 교육의 진정한 부분입니다. 당신은 즉, 나머지 20 %를 얻기 위해 더 높은 과정을해야하지만, 학생으로서 당신은 기본 및 중간 과정을 배웠습니다. 결말에서, 당신은 당신의 코스를 잘했기 때문에 잘 가르치는 느낌이들 것입니다. 그것은 현실의 일부일 뿐이며, 최선의 방법은 코더를 공격하지 않고 부작용을 이해하는 것입니다.
독립

8

의견 중 하나에서 Mason은 언젠가 우리가 상속 에 대해 말하는 것은 해로운 것으로 간주 한다고 언급했습니다 .

나는 희망한다.

상속 문제는 한 번에 간단하고 치명적이며 하나의 개념에 하나의 기능이 있어야한다는 생각을 존중하지 않습니다.

대부분의 OO 언어에서는 기본 클래스에서 상속 할 때 다음을 수행합니다.

  • 인터페이스에서 상속
  • 구현에서 상속 (데이터와 메소드 모두)

그리고 여기서 문제가됩니다.

00 언어가 대부분 그것에 붙어 있지만 이것이 유일한 접근 방법 은 아닙니다 . 다행히 인터페이스 / 추상 클래스가 존재합니다.

또한, 달리 쉽게 할 수 없다는 점은 그것을 많이 사용하는 데 기여합니다. 솔직히, 이것을 알더라도 인터페이스에서 상속하고 기본 클래스를 컴포지션으로 임베드하여 대부분의 메소드 호출을 위임합니까? 그래도 인터페이스에 갑자기 새로운 메소드가 나타나고 의식적으로 구현 방법을 선택해야한다는 경고가 나올 것입니다.

대향 점과, Haskell순수한 인터페이스 (라는 개념)로부터 "파생"때 Liskov 원리를 사용할 수 있도록 (1). 기존 클래스에서 파생 할 수 없으며 컴포지션 만 사용하여 해당 데이터를 포함 할 수 있습니다.

(1) 개념은 구현에 대한 합리적인 기본값을 제공 할 수 있지만, 데이터가 없기 때문에이 기본값은 개념에서 제안 된 다른 방법이나 상수 측면에서만 정의 할 수 있습니다.


7

간단한 대답은 상속이 컴포지션보다 더 큰 결합을 가지고 있다는 것입니다. 달리 동등한 품질의 두 가지 옵션이 주어지면 덜 결합 된 옵션을 선택하십시오.


2
그러나 그것은 요점입니다. 그들은 "그렇지 않으면 동등 하지 않다 ". 상속은 OOP 사용의 핵심 인 Liskov 치환 및 다형성을 가능하게합니다. 구성하지 않습니다.
메이슨 휠러

4
다형성 / LSP는 OOP의 "요점"이 아니며 하나의 기능입니다. 캡슐화, 추상화 등도 있습니다. 상속은 "is"관계를 의미하고, 집계 모델은 "has"관계를 나타냅니다.
Steve

@Steve : 데이터 구조 및 절차 / 함수 생성을 지원하는 모든 패러다임에서 추상화 및 캡슐화를 수행 할 수 있습니다. 그러나 다형성 (특히이 컨텍스트에서 가상 메소드 디스패치 참조)과 Liskov 대체는 객체 지향 프로그래밍에 고유합니다. 그것이 내가 의미 한 바입니다.
메이슨 휠러

3
@Mason : 지침은 "상속보다 구성을 선호하는 것"입니다. 결코 이것이 상속을 사용하지 않거나 심지어 피하는 것을 의미하지는 않습니다. 다른 모든 것들이 같으면 작곡을 선택하십시오. 그러나 상속이 필요하면 사용하십시오!
Jeffrey Faust

7

이런 종류의 조언은 "비행을 선호합니다"라고 말하는 것과 같습니다. 즉, 비행기는 자동차에 비해 모든 종류의 장점이 있지만 특정 복잡성이 있습니다. 많은 사람들이 도심에서 교외로 날아 가려고한다면 , 실제로 들어야 할 조언 은 날지 않아도된다는 것입니다. 사실 날아 가면 장기적으로 더 복잡해집니다 단기간에 시원하고 / 효율적 / 쉬운 것처럼 보일지라도. 당신이 때 반면 않는 비행 필요, 일반적으로 명백해야하는데.

마찬가지로 상속은 구성이 할 수없는 일을 할 수 있지만 필요할 때가 아니라 필요할 때 사용해야합니다. 따라서 필요하지 않을 때 상속이 필요하다고 가정하지 않으려는 경우 "구성 선호"에 대한 조언이 필요하지 않습니다. 그러나 많은 사람들은 어떻게 하고, 어떻게 그 조언이 필요합니다.

상속이 정말로 필요할 때 분명하고, 그때 그것을 사용해야한다는 것을 암시해야합니다.

또한 Steven Lowe의 답변입니다. 정말로, 정말로.


1
나는 당신의 비유를 좋아합니다
barrylloyd

2

상속은 본질적으로 나쁘지 않으며 구성은 본질적으로 좋지 않습니다. OO 프로그래머가 소프트웨어를 설계하는 데 사용할 수있는 도구 일뿐입니다.

수업을 볼 때 반드시해야 할 것보다 더 많은 일을하고 있습니까 ( SRP )? 기능을 불필요하게 복제합니까 ( DRY ), 또는 다른 클래스의 특성 또는 메소드에 지나치게 관심이 있습니까 ( Feature Envy ) ?. 수업이 이러한 모든 개념을 위반하는 경우 (그리고 아마도 더 많은 경우), 신 수업이 되려고 노력하고 있습니까 ? 이들은 소프트웨어를 설계 할 때 발생할 수있는 여러 가지 문제이며, 반드시 상속 문제는 아니지만 다형성이 적용된 주요 두통과 취성 의존성을 빠르게 만들 수 있습니다.

문제의 근원은 상속에 대한 이해 부족이 적고 디자인 측면에서 선택이 잘못되었거나 단일 책임 원칙을 준수하지 않는 클래스와 관련된 "코드 냄새"를 인식하지 못하는 것 입니다. 다형성Liskov 치환 은 조성을 위해 폐기 될 필요가 없다. 다형성 자체는 상속에 의존하지 않고 적용될 수 있으며, 이것들은 모두 매우 보완적인 개념입니다. 신중하게 적용되는 경우. 비결은 코드를 단순하고 깨끗하게 유지하고 강력한 시스템 디자인을 만들기 위해 만들어야하는 클래스 수에 지나치게 신경을 쓰지 않도록하는 것입니다.

상속보다 구성을 선호하는 문제는 해결해야 할 문제와 관련하여 디자인 요소를 신중하게 적용하는 또 다른 사례 일뿐입니다. 행동을 상속받을 필요가 없다면 , 컴포지션이 나중에 비 호환성과 주요 리팩토링 노력을 피하는 데 도움이되지 않을 것입니다. 반면에 모든 중복이 유사한 클래스 그룹에 집중되도록 많은 코드를 반복하는 경우 공통 조상을 작성하면 동일한 호출 및 클래스 수를 줄이는 데 도움이 될 수 있습니다 각 수업 사이에 반복해야 할 수도 있습니다. 따라서 구성을 선호하지만 상속이 적용되지 않는다고 가정하지는 않습니다.


-1

실제 인용문은 Joshua Bloch의 "Effective Java"에서 나온 것인데, 여기서는 장 중 하나의 제목입니다. 그는 상속은 패키지 내에서 그리고 클래스가 확장을 위해 특별히 설계되고 문서화 된 모든 곳에서 안전하다고 주장합니다. 그는 다른 사람들이 지적했듯이 서브 클래스는 수퍼 클래스의 구현 세부 사항에 의존하기 때문에 상속이 캡슐화를 중단한다고 주장한다. 그는 이것이 취약성을 초래한다고 말한다.

그가 언급하지는 않지만 Java의 단일 상속은 구성이 상속보다 훨씬 더 많은 유연성을 제공한다는 것을 의미합니다.

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