상속보다는 구성


13

소프트웨어 엔지니어링을 가르치고 혼란스러워하는 상충되는 정보에 대비하려고합니다.

나는 OOP를 배우고 추상 클래스 / 인터페이스가 무엇인지, 그것들을 사용하는 방법을 배우고 있지만, 상속보다 구성을 선호해야한다는 것을 읽고 있습니다. 나는 한 클래스가 다른 클래스의 객체를 작성 / 생성하여 새로운 객체의 기능을 활용 / 상호 작용할 때 구성을 이해합니다.

그래서 내 질문은 ... 추상 클래스와 인터페이스를 사용하지 않아야합니까? 추상 클래스를 만들지 않고 구체적인 클래스에서 해당 추상 클래스의 기능을 확장 / 상속하지 않고 다른 클래스의 기능을 사용하기 위해 새 객체를 작성하려면?

아니면 컴포지션을 사용하고 추상 클래스에서 상속해야합니다. 서로 함께 사용? 그렇다면 어떻게 작동하는지와 그 이점에 대한 몇 가지 예를 제공 할 수 있습니까?

PHP에 가장 익숙하기 때문에 다른 언어로 이동하고 새로 습득 한 SE 기술을 이전하기 전에 OOP / SE 기술을 향상시키기 위해이 기능을 사용하고 있으므로 PHP를 사용한 예제가 가장 높이 평가됩니다.


2
"상속에 대한 선호 구성"은 "드릴보다 선호하는 톱"과 정확히 일치하는 어리석은 아이디어입니다. 그것들은 서로 다른 두 가지 사용 사례에 사용하기에 적합한 두 가지 도구이며, 실제로 하나를 상호 교환하여 사용할 수있는 시간은 실제로 매우 드 rare니다.
메이슨 휠러

2
"Y를 통해 호의 X는"단지 X가 더 좋을 것이다 경우에 대해 생각, Y를 사용하지 않을 것을 의미하지 않는다
리처드 설렘

2
"상속 상속에 대한 선호도 구성"은 인터페이스를 구현하는 것이 상속이 아니며 선택한 언어가 인터페이스가 아닌 추상 클래스 만 지원하는 경우 100 %에서 상속한다는 점을 명확히하여 "클래스 상속을 절대 사용하지 마십시오"로보다 정확하게 표현됩니다. 추상 클래스도 "상속이 아님"으로 볼 수 있습니다.
David Arno

1
@MasonWheeler, 상속에 대한 유효한 유스 케이스가 없습니다. 최소한 "goto"와 같은 "악"기능입니다.
David Arno

1
@DavidArno 그건 우스운 일입니다. 상속은 프로그래밍의 전체 역사에서 개발 된 가장 유용하고 생산적인 기능 중 하나입니다. 유용한 것과 마찬가지로 그것을 남용하는 방법은 많이 있지만 제대로 사용하면 작업의 힘과 생산성이 크게 향상됩니다.
메이슨 휠러

답변:


19

상속에 대한 구성은 기존 클래스의 기능을 재사용하거나 확장하려는 경우 기존 클래스를 '포장'하고 구현을 내부적으로 사용하는 다른 클래스를 만드는 것이 더 적절하다는 것을 의미합니다. 데코레이터 패턴 이 그 예입니다.

상속은 기본 클래스 기능의 일부만 사용하기를 원하고 하위 클래스가 Liskov 대체 를 만족시키는 방식으로 기본 클래스의 전체 계약을 지원할 수 없기 때문에 재사용 시나리오를 처리하는 좋은 기본 방법이 아닙니다. 원칙 .

템플릿 방법 은 상속이 적절한 경우의 좋은 예입니다.

구성과 상속 중에서 선택하는 방법에 대한 지침 은 이 답변 을 참조하십시오 .


클래스의 부분 재사용의 경우 사용되지 않은 부분은 상속보다 컴포지션에서 더 명확하게 무시된다는 점을 지적하면 +1입니다.
로렌스

4

PHP에 익숙하지 않아서 해당 언어로 된 구체적인 예제를 제공 할 수 있지만 다음은 유용한 몇 가지 지침입니다.

인터페이스 / 특성 은 다른 클래스가 거의없는 여러 클래스에서 동작을 정의하는 데 유용합니다.

예를 들어 JSON API를 사용하는 경우 "toJson"및 "toStatusCode"의 두 가지 방법을 지정하는 인터페이스를 정의 할 수 있습니다. 이를 통해이 인터페이스를 구현하는 모든 객체를 가져 와서 Http 응답으로 변환 할 수있는 도우미를 작성할 수 있습니다.

대부분의 경우, 상속을 지시하는 것이 바람직합니다. 이는 계급 계층 구조가 얕아지기 때문에 취약한 기본 계급 문제를 겪을 가능성이 적기 때문입니다.

직접 상속 은하지 말아야 할 강력한 이유가없는 한하지 말고 설득력있는 이유가있을 때하는 것입니다.

인터페이스에서 기본 구현을 지원하지 않는 언어에서는 기본 기능 세트를 공유하는 합리적인 방법 일 수 있지만 일반적으로 서브 클래스로 수행 할 수있는 작업을 크게 제한합니다 (사용 가능한 경우 다중 상속은 거의 가치가 없습니다) . 종종 구성을 사용하기 위해 상용구 리디렉션 방법을 작성하는 것이 어려울 가치가 있다고 생각합니다.

구성 은 기본 전략이어야합니다. 가능한 많이 인터페이스로 작성하고 생성자 인수로 전달하십시오. 이것은 시험을 작성할 때 당신의 인생을 훨씬 쉽게 만듭니다.

출력의 일부가 시스템 클록의 상태에 의존하는 경우 예상되는 출력과 실제 출력을 비교하는 것은 올바른 고통이므로 가능한 한 글로벌 싱글 톤으로 작업하지 마십시오.

물론 이것이 항상 가능한 것은 아닙니다. 예를 들어 Play 웹 프레임 워크는 기본적으로 도메인 별 언어 인 JSON 라이브러리를 제공합니다. 이것을 바꾸는 것은 중요한 일이 될 것이며, 그 중 'Json.prettyPrint'에 대한 호출을 바꾸는 것은 아주 작은 일입니다.


1

구성은 클래스에서 상속하는 대신 이미이 기능을 구현하는 (아마도 내부) 클래스를 인스턴스화하여 클래스가 일부 기능을 제공하는 경우입니다.

예를 들어, 당신이 배를 모형화하는 클래스를 가지고 있고 배가 헬기 착륙장을 제공해야한다고 들었다면, 대신 헬기 착륙장에서 배를 파생시키는 것은 당연하지 않습니다. 당신의 함선은 헬기 이착륙장을 포함하고 있으며 어떤 Ship.getHelipad()방법으로 노출시킨다 .

수십 년 전쯤에 사람들은 상속을 기능을 집계하는 빠르고 쉬운 방법으로 보았 기 때문에 당연히 절름발이 인 "헬리 파드에서 상속 된 선박"종류의 많은 예가있었습니다.

그러나 "상속에 대한 선호 구성"dictum은 이것이 규칙이 아니라 단지 제안 일뿐임을 분명히하기 위해 신중하게 표현되었습니다. dictum의 저자는 "당신 은 상속을 절대로 사용 하지 말고 작곡 사용 하라"는 말을 자제하기에 충분히주의를 기울였습니다 . 이것은 기본적으로 상속이 과도하게 사용되었다는 사실을 소프트웨어 엔지니어링 커뮤니티의 주목을 끌고있는 반면, 많은 경우 컴포지션은 상속보다 명확하고 우아하며 유지 보수 가능한 디자인을 생성합니다.

따라서 본질적으로 "상속에 대한 선호 구성"dictum은 "상속 또는 구성?"에 직면 할 때마다 제안합니다. 질문, 당신은 가장 적합한 전략이 무엇인지 열심히 생각해야하며, 대부분의 가능성은 가장 적합한 전략이 상속이 아니라 구성이 될 수 있다는 것입니다.

그러나 이것이 규칙이 아니기 때문에 상속이 더 자연스러운 경우가 많다는 것을 명심해야합니다. 상속을 사용해야 할 곳에 컴포지션을 사용하면 많은 악이 코드에 해당됩니다.

우주선 예제로 돌아가려면, 우주선이와 상호 작용하기위한 인터페이스를 제공해야하는 경우 FloatingMachine추상 FloatingMachine클래스 에서 파생하는 것이 더 자연 스럽습니다 Machine.이 클래스 는 다른 추상 클래스 에서 파생 될 수 있습니다 .

다음은 작문과 상속 질문에 대한 답변의 경험 법칙입니다.

내 클래스가 노출해야하는 인터페이스와 "존재"관계가 있습니까? 그렇다면 상속을 사용하십시오. 그렇지 않은 경우 구성을 사용하십시오.

선박은 "플로팅 머신"이고 플로팅 머신은 "플로팅 머신"입니다. 따라서 상속은 완벽하게 좋습니다. 그러나 배는 물론 헬기 착륙장이 아닙니다. 따라서 헬기 착륙장의 기능을 더 잘 구성 할 수 있습니다.

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