는 "상속을 통해 구성을 선호" 단지 좋은 발견
컨텍스트는 보편적 인 규칙이 아니므로 컨텍스트를 고려해야합니다. 컴포지션을 수행 할 때 상속을 사용하지 않는다는 의미로 사용하지 마십시오. 이 경우 상속을 금지하여 문제를 해결합니다.
이 게시물을 통해이 요점을 분명히하기를 바랍니다.
나는 그 구성의 장점을 스스로 방어하려고하지 않을 것이다. 나는 주제에서 벗어난 것을 고려합니다. 대신 개발자가 컴포지션을 사용하는 것이 더 나은 상속을 고려할 수있는 상황에 대해 이야기하겠습니다. 그 노트에 상속에는 고유 한 장점이 있으며 주제에서도 고려해야합니다.
차량 예
나는 서술적인 목적으로 바보 같은 일을 시도하는 개발자에 대해 쓰고 있습니다.
일부 OOP 과정 사용하는 것이 우리가 고전적인 사례의 변형을 위해 가자 ... 우리는이 Vehicle
클래스를 우리는 유도 Car
, Airplane
, Balloon
와 Ship
.
참고 :이 예제를 접지해야하는 경우 비디오 게임에서 이러한 종류의 개체 인 것처럼 가장하십시오.
다음 Car
과 Airplane
둘 다 땅에 바퀴에 출시 할 수 있기 때문에, 몇 가지 일반적인 코드가있을 수 있습니다. 개발자는이를 위해 상속 체인에서 중개 클래스를 작성하는 것을 고려할 수 있습니다. 그러나 실제로 Airplane
와 사이에 공유 코드도 있습니다 Balloon
. 그들은 그것을 위해 상속 체인에 다른 중개 클래스를 만드는 것을 고려할 수 있습니다.
따라서 개발자는 다중 상속을 찾고있을 것입니다. 개발자가 다중 상속을 찾고있는 시점에서 디자인은 이미 잘못되었습니다.
이 동작을 인터페이스와 컴포지션으로 모델링하는 것이 좋습니다. 따라서 여러 클래스 상속을받지 않고도 재사용 할 수 있습니다. 예를 들어 개발자가 FlyingVehicule
클래스를 만드는 경우 그들은 그 말을 할 것이다 Airplane
A는 FlyingVehicule
(클래스 상속), 그러나 우리는 그 대신 말할 수 Airplane
가 Flying
구성 요소 (조성)와 Airplane
A는 IFlyingVehicule
(인터페이스 상속).
필요한 경우 인터페이스를 사용하여 인터페이스의 다중 상속을 가질 수 있습니다. 또한 특정 구현에 연결하지 않습니다. 코드의 재사용 성 및 테스트 성 향상
상속은 다형성의 도구라는 것을 기억하십시오. 또한 다형성은 재사용 성을위한 도구입니다. 컴포지션을 사용하여 코드의 재사용 성을 높일 수 있다면 그렇게하십시오. 컴포지션이 더 나은 재사용 성을 제공하는지 확실하지 않은 경우 "상속보다 컴포지션 선호"는 좋은 휴리스틱입니다.
언급하지 않고 모든 것 Amphibious
.
실제로, 우리는 땅에서 떨어지는 것들이 필요하지 않을 수도 있습니다. Stephen Hurn은 자신의 기사 "상속적인 선호 구성" 1 부 및 2 부 에서 더 웅변적인 예를 보였습니다 .
대체 성 및 캡슐화
A
상속 또는 작성 해야합니까 B
?
경우 A
의 전문화가 B
수행해야하는 Liskov 대체 원칙 , 상속도 바람직 실용적이다. A
유효한 대체가 아닌 상황이있는 B
경우 상속을 사용해서는 안됩니다.
우리 는 파생 클래스를 방어하기 위해 방어 프로그래밍의 형태로 구성에 관심이있을 수 있습니다 . 특히, B
다른 목적으로 사용 하기 시작하면 해당 목적에보다 적합하도록 변경하거나 확장해야하는 압력이있을 수 있습니다. B
잘못된 상태가 될 수있는 방법을 노출시킬 위험이있는 경우 A
상속 대신 구성을 사용해야합니다. 우리 둘의 저자하더라도 B
하고 A
, 그것에 대해 걱정 한 것은 작 따라서 조성의 재사용을 용이하게 B
.
우리는 심지어이있는 경우에 기능한다고 주장 할 수있다 B
즉 A
필요로하지 않는다 (그 기능에 대한 잘못된 상태가 발생할 수 있다면 우리가 모르는 A
본 구현 또는 미래 중), 그것은 사용 구성에 좋은 아이디어입니다 상속 대신.
또한 컴포지션은 전환 구현을 허용하고 조롱을 완화 할 수있는 장점이 있습니다.
참고 : 대체가 유효하더라도 구성을 사용하려는 상황이 있습니다. 인터페이스 나 추상 클래스 (다른 주제 일 때 사용할 주제)를 사용하여 대체 가능성 을 보관 한 다음 실제 구현의 종속성 주입과 함께 구성을 사용합니다.
마지막으로 상속 은 부모 클래스 의 캡슐화를 깨뜨리기 때문에 부모 클래스를 방어하기 위해 구성 을 사용해야한다는 주장이 있습니다 .
상속은 부모의 구현에 대한 세부 사항에 서브 클래스를 노출시킵니다.
-디자인 패턴 : 재사용 가능한 객체 지향 소프트웨어의 요소
글쎄, 그것은 잘못 설계된 부모 클래스입니다. 그렇기 때문에 다음을 수행해야합니다.
상속을 위해 디자인하거나 금지하십시오.
-효과적인 Java, Josh Bloch
요요 문제
구성이 도움이되는 또 다른 경우는 Yo-Yo 문제 입니다. 이것은 Wikipedia에서 인용 한 것입니다.
소프트웨어 개발에서 요요 문제는 프로그래머가 상속 그래프가 너무 길고 복잡한 프로그램을 읽고 이해해야 할 때 발생하는 반 패턴으로, 프로그래머가 따르기 위해 여러 클래스 정의 사이에서 계속 뒤집어 야합니다. 프로그램의 제어 흐름.
예를 들어, 클래스 C
는 class에서 상속되지 않습니다 B
. 대신 당신의 클래스 C
형의 멤버가됩니다 A
형식의 개체를하거나하지 않을 수 있습니다 (또는 있습니다) B
. 이 방법으로의 구현 세부 사항에 대해 프로그래밍하는 것이 B
아니라 인터페이스가 A
제공 하는 계약에 대해 프로그래밍 합니다.
카운터 예
많은 프레임 워크는 컴포지션에 대한 상속을 선호합니다 (이것은 우리가 주장한 것과 반대입니다). 개발자는 컴포지션으로 구현하면 클라이언트 코드의 크기가 커질 수 있기 때문에 기본 클래스에 많은 작업을 수행하기 때문에이 작업을 수행 할 수 있습니다. 때때로 이것은 언어의 한계 때문입니다.
예를 들어, PHP ORM 프레임 워크는 객체에 실제 속성이있는 것처럼 코드 작성을 허용하는 매직 메서드를 사용하는 기본 클래스를 만들 수 있습니다. 대신 매직 메소드가 처리하는 코드는 데이터베이스로 이동하여 특정 요청 필드를 쿼리하고 (아마도 향후 요청을 위해 캐시 함) 반환합니다. 컴포지션으로이를 수행하려면 클라이언트가 각 필드에 대한 특성을 작성하거나 일부 버전의 매직 메소드 코드를 작성해야합니다.
부록 : ORM 객체를 확장 할 수있는 다른 방법이 있습니다. 따라서이 경우 상속이 필요하다고 생각하지 않습니다. 이것이 더 싸다.
다른 예를 들어, 비디오 게임 엔진은 타겟 플랫폼에 따라 네이티브 코드를 사용하여 3D 렌더링 및 이벤트 처리를 수행하는 기본 클래스를 생성 할 수있다. 이 코드는 복잡하고 플랫폼에 따라 다릅니다. 엔진 개발자가이 코드를 처리하는 것은 비용이 많이 들고 오류가 발생하기 쉬우 며, 이는 엔진을 사용하는 이유의 일부입니다.
또한 3D 렌더링 부분이 없으면 작동하는 위젯 프레임 워크 수입니다. 이렇게하면 OS 메시지 처리에 대해 걱정할 필요가 없습니다. 사실 많은 언어에서 기본 입찰 방식 없이는 이러한 코드를 작성할 수 없습니다. 또한, 그렇게한다면 휴대 성이 빡빡 할 것입니다. 대신, 개발자가 호환성을 너무 많이 잃지 않는다면 상속으로; 나중에 지원하는 새로운 플랫폼으로 코드를 쉽게 이식 할 수 있습니다.
또한 몇 번의 메서드 만 재정의하고 다른 모든 것은 기본 구현으로 유지하려는 경우가 많습니다. 컴포지션을 사용했다면 랩핑 된 객체에 위임하기 만하더라도 모든 메서드를 만들어야합니다.
이 주장에 따르면, 상속보다 유지 보수성에있어 구성이 최악 일 수 있습니다 (기본 클래스가 너무 복잡한 경우). 그러나 상속의 유지 관리 가능성은 구성의 유지 관리 가능성보다 더 나을 수 있음을 기억하십시오 (상속 트리가 너무 복잡 할 때).
제시된 예제에서 개발자는 다른 프로젝트에서 상속을 통해 생성 된 코드를 재사용하려고 거의하지 않습니다. 이는 컴포지션 대신 상속을 사용하는 감소 된 재사용 성을 완화시킵니다. 또한 상속을 사용하여 프레임 워크 개발자는 사용하기 쉽고 코드를 쉽게 찾을 수 있습니다.
마지막 생각들
보시다시피, 컴포지션은 일부 상황에서 상속에 비해 항상 이점이 있습니다. 결정을 내리기 위해서는 상황과 관련된 여러 요소 (예 : 재사용 성, 유지 관리 성, 테스트 가능성 등)를 고려해야합니다. 첫 번째 점으로 돌아 가기 : "상속을 통해 구성을 선호"는 것입니다 단지 좋은 발견.
또한 내가 설명하는 많은 상황이 특성 또는 믹스 인으로 어느 정도 해결 될 수 있음을 알 수 있습니다. 안타깝게도 이러한 기능은 다양한 언어 목록에서 일반적으로 사용되는 기능이 아니며 일반적으로 약간의 성능 비용이 발생합니다. 고맙게도, 그들의 인기있는 사촌 확장 방법과 기본 구현은 일부 상황을 완화시킵니다.
C #에서 UI, 비즈니스 및 데이터 액세스 간의 인터페이스가 필요한 이유에 대한 인터페이스의 장점에 대해 이야기하는 최근 게시물이 있습니다. 디커플링에 도움이되고 재사용 성 및 테스트 성을 용이하게합니다.