“상속보다 작곡 선호”-서명 변경을 방어해야하는 유일한 이유는 무엇입니까?


13

이 페이지 는 다음과 같은 주장으로 상속에 대한 구성을 옹호합니다 (내 말로 표현).

서브 클래스에서 재정의되지 않은 수퍼 클래스의 메소드 서명 변경으로 인해 상속을 사용할 때 여러 곳에서 추가 변경이 발생합니다. 그러나 컴포지션을 사용할 때 필요한 추가 변경은 단일 위치에만 있습니다 : 서브 클래스.

이것이 실제로 상속보다 구성을 선호하는 유일한 이유입니까? 이 경우 서브 클래스가 구현을 변경하지 않는 경우에도 (즉, 서브 클래스에 더미 대체를 적용하여) 수퍼 클래스의 모든 메소드를 대체하는 코딩 스타일을 적용하여이 문제점을 쉽게 완화 할 수 있습니다. 여기에 뭔가 빠졌습니까?


3
가능한 상속
gnat

1
참조 : $ {blog} 토론
gnat

2
이 인용문이 "유일한"이유는 아닙니다.
Tulains Córdova

3
상속은 일반적으로 복잡성, 결합, 코드 판독을 추가하고 유연성을 감소시키기 때문에 구성은 거의 항상 더 좋습니다. 그러나 주목할만한 예외가 있지만 때로는 기본 클래스 또는 2가 아프지 않습니다. 그것이 ' 항상 ' 보다는 ' 선호 '를 선호하는 이유 입니다.
마크 로저스

답변:


27

나는 유추를 좋아하므로 여기에 하나가 있습니다 : VCR 플레이어가 내장 된 TV 중 하나를 본 적이 있습니까? VCR과 DVD 플레이어가있는 것은 어떻습니까? 또는 Blu-ray, DVD 및 VCR이있는 것. 아 그리고 이제 모든 사람들이 스트리밍하고 있으므로 새로운 TV 세트를 디자인해야합니다 ...

TV를 소유하고 있다면 위와 같은 것이 없을 것입니다. 아마도 대부분의 사람들은 존재하지 않았을 것입니다. 새로운 입력 장치 유형이있을 때마다 새로운 세트가 필요하지 않도록 많은 입력이있는 모니터 또는 세트가있을 가능성이 큽니다.

상속은 VCR이 내장 된 TV와 같습니다. 함께 사용하려는 3 가지 유형의 동작이 있고 각각 구현을위한 2 가지 옵션이있는 경우,이를 모두 나타내려면 8 가지의 다른 클래스가 필요합니다. 더 많은 옵션을 추가하면 숫자가 폭발합니다. 컴포지션을 대신 사용하면 조합 문제를 피할 수 있으며 디자인이 더 확장 가능한 경향이 있습니다.


6
90 년대 후반과 2000 년대 초에 VCR과 DVD 플레이어가 내장 된 TV가 많이있었습니다.
JAB

@JAB과 오늘날 판매되는 많은 (대부분의) TV는 스트리밍을 지원합니다.
Casey

4
@JAB 그리고 그들 중 누구도 Bluray 플레이어 구입을 돕기 위해 DVD 플레이어를 판매 할 수 없습니다
Alexander-Reinstate Monica

1
@JAB 맞습니다. "VCR 플레이어가 내장 된 TV 중 하나를 본 적이 있습니까?" 그것들이 존재한다는 것을 암시합니다.
JimmyJames

4
@Casey 분명히이 기술을 대체 할 다른 기술은 없을 것입니다. 새 TV를 구입하지 않고 다른 것을 사용하려는 경우를 대비하여 이러한 TV 중 하나가 외부 장치의 입력도 지원한다는 점을 설명하겠습니다. 또는 오히려 교육을받은 구매자는 그러한 입력이없는 TV를 기꺼이 구매하려고합니다. 저의 전제는 이러한 접근법이 존재하지 않으며 상속이 존재하지 않는다고 주장하지도 않습니다. 요점은 그러한 접근법은 본질적으로 구성보다 유연성이 떨어진다는 것입니다.
JimmyJames

4

전혀 그렇지 않다. 내 경험상 상속에 대한 구성은 소프트웨어 가 클래스데이터 대신 서로 에게 서비스 를 제공하는 객체 / 객체와 상호 작용 하는 행동 을 가진 객체 의 무리로 생각한 결과입니다 .

이런 식으로 serviecs를 제공하는 객체가 있습니다. 다른 동작 (예 : UserRegistrationServiceEmailerServiceUserRepository에서 제공하는 서비스를 사용함 )을 가능하게하기 위해 빌딩 블록 (실제로는 레고와 거의 유사)으로 사용합니다 .

이 접근 방식으로 더 큰 시스템을 구축 할 때 자연스럽게 상속을 거의 사용하지 않았습니다. 하루가 끝나면 상속은 매우 강력하고 적절한 도구 만 사용해야합니다.


여기에 Java 사고 방식이 있습니다. OOP는 데이터를 처리하는 기능과 함께 패키지 된 데이터에 관한 것입니다. 설명하는 것을 "모듈"이라고합니다. 객체를 모듈로 용도 변경하는 경우 상속을 피하는 것이 좋습니다.하지만 객체를 사용하는 기본 방법으로 권장하는 것처럼 보이지 않습니다.
화려한

1
@Brilliand 당신이 묘사 한 스타일로 얼마나 많은 자바 코드가 작성되는지 그리고 얼마나 많은 공포가 있는지 아는 경우 ... 스몰 토크는 OOP라는 용어를 도입했으며 메시지를받는 객체를 중심 으로 설계되었습니다. : "컴퓨팅은 메시지를 보내서 균일하게 호출 할 수있는 객체의 고유 기능으로 간주되어야합니다." 데이터와 기능에 대한 언급은 없습니다. 미안하지만 내 의견으로는 당신은 심각하게 착각합니다. 데이터와 함수에 관한 것이라면 행복하게 구조체 작성을 계속할 수 있습니다.
marstato

@Tsar 불행히도 Java는 정적 메소드를 지원하지만 Java 문화권은 그렇지 않습니다.
k_g

@Brilliand 누가 OOP가 무엇에 대해 관심이 있습니까? 중요한 것은 당신이 어떻게 할 수 그것은 그 방법으로 그것을 사용의 결과를 사용합니다.
user253751

1
@Tsar와 토론 한 후 : Java 클래스를 인스턴스화 할 필요가 없으며 실제로 UserRegistrationServiceEmailService 와 같은 클래스는 인스턴스화하지 않아야합니다. 관련 데이터가없는 클래스는 정적 클래스와 가장 잘 작동하며 원래 클래스와 관련이 없습니다. 질문). 나는 이전의 의견 중 일부를 삭제하지만 marsato의 답변에 대한 원래의 비판은 유효합니다.
화려한

4

는 "상속을 통해 구성을 선호" 단지 좋은 발견

컨텍스트는 보편적 인 규칙이 아니므로 컨텍스트를 고려해야합니다. 컴포지션을 수행 할 때 상속을 사용하지 않는다는 의미로 사용하지 마십시오. 이 경우 상속을 금지하여 문제를 해결합니다.

이 게시물을 통해이 요점을 분명히하기를 바랍니다.

나는 그 구성의 장점을 스스로 방어하려고하지 않을 것이다. 나는 주제에서 벗어난 것을 고려합니다. 대신 개발자가 컴포지션을 사용하는 것이 더 나은 상속을 고려할 수있는 상황에 대해 이야기하겠습니다. 그 노트에 상속에는 고유 한 장점이 있으며 주제에서도 고려해야합니다.


차량 예

나는 서술적인 목적으로 바보 같은 일을 시도하는 개발자에 대해 쓰고 있습니다.

일부 OOP 과정 사용하는 것이 우리가 고전적인 사례의 변형을 위해 가자 ... 우리는이 Vehicle클래스를 우리는 유도 Car, Airplane, BalloonShip.

참고 :이 예제를 접지해야하는 경우 비디오 게임에서 이러한 종류의 개체 인 것처럼 가장하십시오.

다음 CarAirplane둘 다 땅에 바퀴에 출시 할 수 있기 때문에, 몇 가지 일반적인 코드가있을 수 있습니다. 개발자는이를 위해 상속 체인에서 중개 클래스를 작성하는 것을 고려할 수 있습니다. 그러나 실제로 Airplane와 사이에 공유 코드도 있습니다 Balloon. 그들은 그것을 위해 상속 체인에 다른 중개 클래스를 만드는 것을 고려할 수 있습니다.

따라서 개발자는 다중 상속을 찾고있을 것입니다. 개발자가 다중 상속을 찾고있는 시점에서 디자인은 이미 잘못되었습니다.

이 동작을 인터페이스와 컴포지션으로 모델링하는 것이 좋습니다. 따라서 여러 클래스 상속을받지 않고도 재사용 할 수 있습니다. 예를 들어 개발자가 FlyingVehicule클래스를 만드는 경우 그들은 그 말을 할 것이다 AirplaneA는 FlyingVehicule(클래스 상속), 그러나 우리는 그 대신 말할 수 AirplaneFlying구성 요소 (조성)와 AirplaneA는 IFlyingVehicule(인터페이스 상속).

필요한 경우 인터페이스를 사용하여 인터페이스의 다중 상속을 가질 수 있습니다. 또한 특정 구현에 연결하지 않습니다. 코드의 재사용 성 및 테스트 성 향상

상속은 다형성의 도구라는 것을 기억하십시오. 또한 다형성은 재사용 성을위한 도구입니다. 컴포지션을 사용하여 코드의 재사용 성을 높일 수 있다면 그렇게하십시오. 컴포지션이 더 나은 재사용 성을 제공하는지 확실하지 않은 경우 "상속보다 컴포지션 선호"는 좋은 휴리스틱입니다.

언급하지 않고 모든 것 Amphibious.

실제로, 우리는 땅에서 떨어지는 것들이 필요하지 않을 수도 있습니다. Stephen Hurn은 자신의 기사 "상속적인 선호 구성" 1 2 에서 더 웅변적인 예를 보였습니다 .


대체 성 및 캡슐화

A상속 또는 작성 해야합니까 B?

경우 A의 전문화가 B수행해야하는 Liskov 대체 원칙 , 상속도 바람직 실용적이다. A유효한 대체가 아닌 상황이있는 B경우 상속을 사용해서는 안됩니다.

우리 는 파생 클래스를 방어하기 위해 방어 프로그래밍의 형태로 구성에 관심이있을 수 있습니다 . 특히, B다른 목적으로 사용 하기 시작하면 해당 목적에보다 적합하도록 변경하거나 확장해야하는 압력이있을 수 있습니다. B잘못된 상태가 될 수있는 방법을 노출시킬 위험이있는 경우 A상속 대신 구성을 사용해야합니다. 우리 둘의 저자하더라도 B하고 A, 그것에 대해 걱정 한 것은 작 따라서 조성의 재사용을 용이하게 B.

우리는 심지어이있는 경우에 기능한다고 주장 할 수있다 BA필요로하지 않는다 (그 기능에 대한 잘못된 상태가 발생할 수 있다면 우리가 모르는 A본 구현 또는 미래 중), 그것은 사용 구성에 좋은 아이디어입니다 상속 대신.

또한 컴포지션은 전환 구현을 허용하고 조롱을 완화 할 수있는 장점이 있습니다.

참고 : 대체가 유효하더라도 구성을 사용하려는 상황이 있습니다. 인터페이스 나 추상 클래스 (다른 주제 일 때 사용할 주제)를 사용하여 대체 가능성 을 보관 한 다음 실제 구현의 종속성 주입과 함께 구성을 사용합니다.

마지막으로 상속 은 부모 클래스 의 캡슐화를 깨뜨리기 때문에 부모 클래스를 방어하기 위해 구성 사용해야한다는 주장이 있습니다 .

상속은 부모의 구현에 대한 세부 사항에 서브 클래스를 노출시킵니다.

-디자인 패턴 : 재사용 가능한 객체 지향 소프트웨어의 요소

글쎄, 그것은 잘못 설계된 부모 클래스입니다. 그렇기 때문에 다음을 수행해야합니다.

상속을 위해 디자인하거나 금지하십시오.

-효과적인 Java, Josh Bloch


요요 문제

구성이 도움이되는 또 다른 경우는 Yo-Yo 문제 입니다. 이것은 Wikipedia에서 인용 한 것입니다.

소프트웨어 개발에서 요요 문제는 프로그래머가 상속 그래프가 너무 길고 복잡한 프로그램을 읽고 이해해야 할 때 발생하는 반 패턴으로, 프로그래머가 따르기 위해 여러 클래스 정의 사이에서 계속 뒤집어 야합니다. 프로그램의 제어 흐름.

예를 들어, 클래스 C는 class에서 상속되지 않습니다 B. 대신 당신의 클래스 C형의 멤버가됩니다 A형식의 개체를하거나하지 않을 수 있습니다 (또는 있습니다) B. 이 방법으로의 구현 세부 사항에 대해 프로그래밍하는 것이 B아니라 인터페이스가 A제공 하는 계약에 대해 프로그래밍 합니다.


카운터 예

많은 프레임 워크는 컴포지션에 대한 상속을 선호합니다 (이것은 우리가 주장한 것과 반대입니다). 개발자는 컴포지션으로 구현하면 클라이언트 코드의 크기가 커질 수 있기 때문에 기본 클래스에 많은 작업을 수행하기 때문에이 작업을 수행 할 수 있습니다. 때때로 이것은 언어의 한계 때문입니다.

예를 들어, PHP ORM 프레임 워크는 객체에 실제 속성이있는 것처럼 코드 작성을 허용하는 매직 메서드를 사용하는 기본 클래스를 만들 수 있습니다. 대신 매직 메소드가 처리하는 코드는 데이터베이스로 이동하여 특정 요청 필드를 쿼리하고 (아마도 향후 요청을 위해 캐시 함) 반환합니다. 컴포지션으로이를 수행하려면 클라이언트가 각 필드에 대한 특성을 작성하거나 일부 버전의 매직 메소드 코드를 작성해야합니다.

부록 : ORM 객체를 확장 할 수있는 다른 방법이 있습니다. 따라서이 경우 상속이 필요하다고 생각하지 않습니다. 이것이 더 싸다.

다른 예를 들어, 비디오 게임 엔진은 타겟 플랫폼에 따라 네이티브 코드를 사용하여 3D 렌더링 및 이벤트 처리를 수행하는 기본 클래스를 생성 할 수있다. 이 코드는 복잡하고 플랫폼에 따라 다릅니다. 엔진 개발자가이 코드를 처리하는 것은 비용이 많이 들고 오류가 발생하기 쉬우 며, 이는 엔진을 사용하는 이유의 일부입니다.

또한 3D 렌더링 부분이 없으면 작동하는 위젯 프레임 워크 수입니다. 이렇게하면 OS 메시지 처리에 대해 걱정할 필요가 없습니다. 사실 많은 언어에서 기본 입찰 방식 없이는 이러한 코드를 작성할 수 없습니다. 또한, 그렇게한다면 휴대 성이 빡빡 할 것입니다. 대신, 개발자가 호환성을 너무 많이 잃지 않는다면 상속으로; 나중에 지원하는 새로운 플랫폼으로 코드를 쉽게 이식 할 수 있습니다.

또한 몇 번의 메서드 만 재정의하고 다른 모든 것은 기본 구현으로 유지하려는 경우가 많습니다. 컴포지션을 사용했다면 랩핑 된 객체에 위임하기 만하더라도 모든 메서드를 만들어야합니다.

이 주장에 따르면, 상속보다 유지 보수성에있어 구성이 최악 일 수 있습니다 (기본 클래스가 너무 복잡한 경우). 그러나 상속의 유지 관리 가능성은 구성의 유지 관리 가능성보다 더 나을 수 있음을 기억하십시오 (상속 트리가 너무 복잡 할 때).

제시된 예제에서 개발자는 다른 프로젝트에서 상속을 통해 생성 된 코드를 재사용하려고 거의하지 않습니다. 이는 컴포지션 대신 상속을 사용하는 감소 된 재사용 성을 완화시킵니다. 또한 상속을 사용하여 프레임 워크 개발자는 사용하기 쉽고 코드를 쉽게 찾을 수 있습니다.


마지막 생각들

보시다시피, 컴포지션은 일부 상황에서 상속에 비해 항상 이점이 있습니다. 결정을 내리기 위해서는 상황과 관련된 여러 요소 (예 : 재사용 성, 유지 관리 성, 테스트 가능성 등)를 고려해야합니다. 첫 번째 점으로 돌아 가기 : "상속을 통해 구성을 선호"는 것입니다 단지 좋은 발견.

또한 내가 설명하는 많은 상황이 특성 또는 믹스 인으로 어느 정도 해결 될 수 있음을 알 수 있습니다. 안타깝게도 이러한 기능은 다양한 언어 목록에서 일반적으로 사용되는 기능이 아니며 일반적으로 약간의 성능 비용이 발생합니다. 고맙게도, 그들의 인기있는 사촌 확장 방법과 기본 구현은 일부 상황을 완화시킵니다.

C #에서 UI, 비즈니스 및 데이터 액세스 간의 인터페이스가 필요한 이유에 대한 인터페이스의 장점에 대해 이야기하는 최근 게시물이 있습니다. 디커플링에 도움이되고 재사용 성 및 테스트 성을 용이하게합니다.


어 .. .Net 프레임 워크는 여러 유형의 상속 된 스트림을 사용하여 스트림 관련 작업을 수행합니다. 매우 잘 작동하며 사용 및 확장이 매우 쉽습니다. 귀하의 예는 그리 좋지 않습니다 ...
T. Sar

1
@TSar "사용하기 쉽고 확장하기 쉽다"표준 출력 스트림을 디버그하기 위해 티킹을 해 본 적이 있습니까? 스트림을 확장하려는 유일한 경험이었습니다. 쉽지 않았습니다. 클래스의 모든 단일 메소드를 명시 적으로 재정의 한 것은 악몽이었습니다. 매우 반복적입니다. 그리고 .NET 소스 코드를 보면 모든 것을 명시 적으로 재정의하는 것이 그 방식과 거의 비슷합니다. 그래서 나는 그것이 확장하기 쉽다고 확신하지 않습니다.
jpmc26

스트림에서 구조체를 읽고 쓰는 것이 자유 함수 또는 멀티 메소드로 더 잘 수행됩니다.
user253751

@ jpmc26 죄송하지만 귀하의 경험이 그리 좋지 않아서 죄송합니다. 그러나 다른 것들을 위해 스트림 관련 항목을 사용하거나 구현하는 데 아무런 문제가 없었습니다.
T. Sar

3

일반적으로 Composition-Over-Inheritance의 추진 아이디어는 더 큰 설계 유연성을 제공하고 변경 사항을 전파하기 위해 변경해야하는 코드의 양을 줄이지 않는 것입니다 (의심스러운 부분).

Wikipedia 의 예 는 그의 접근 방식의 힘에 대한 더 나은 예를 제공합니다.

각 "구성 요소"에 대한 기본 인터페이스를 정의하면 상속만으로는 도달하기 어려운 다양한 "구성 개체"를 쉽게 만들 수 있습니다.


그래서 부분은 오른쪽 컴포지션 예제를 제공? 기사에서 명확하지 않기 때문에 묻습니다.
Utku

1
예, "상속성 구성"은 인터페이스가 없다는 것을 의미하지는 않습니다. Player 객체를 보면 계층 구조에 잘 맞는 것을 볼 수 있지만 (잔인한 실례) 코드는 상속에서 나오지 않습니다. 그러나 일을하는 클래스가있는 작곡에서
lbenini

2

"상속보다 작문을 선호하라"는 올바른 방향으로 조금만 나아가면된다. 그것은 당신의 어리 석음으로부터 당신을 구하지 않을 것이고 실제로 당신에게 일을 훨씬 더 악화시킬 수있는 기회를 제공합니다. 여기에는 많은 힘이 있기 때문입니다.

이것이 말할 필요가있는 주된 이유는 프로그래머가 게으 르기 때문입니다. 그래서 게으른 그들은 키보드 타이핑을 줄이는 모든 솔루션을 취할 것입니다. 이것이 상속의 주요 판매 지점입니다. 나는 오늘 덜 타이핑하고 다른 누군가는 내일 망쳐진다. 그래서 집에 가고 싶어

다음날 상사는 내가 작곡을 사용한다고 주장한다. 이유를 설명하지는 않지만 주장하고 있습니다. 그래서 나는 상속받은 것을 인스턴스화하고 상속받은 것과 동일한 인터페이스를 공개하며 모든 작업을 상속받은 것에 위임하여 해당 인터페이스를 구현합니다. 리팩토링 도구로 할 수 있었던 것은 모두 뇌의 죽은 타이핑이며 무의미 해 보이지만 상사는 그것을 원했습니다.

다음날 나는 이것이 원하는 것이 냐고 묻습니다. 사장님이 뭐라고 생각하세요?

상속 된 것을 동적으로 (런타임에) 변경할 수 있어야하지 않는 한 (상태 패턴 참조) 이것은 엄청난 시간 낭비였습니다. 상사가 어떻게 그렇게 말할 수 있고 여전히 상속보다 구성에 찬성 하는가?

또한 방법 서명 변경으로 인한 파손을 방지하기위한 작업을 수행하지 않는 것 외에는 컴포지션의 가장 큰 장점이 없습니다. 상속과 달리 다른 인터페이스를 자유롭게 만들 수 있습니다 . 이 레벨에서 어떻게 사용되는지에 대해 더 적절합니다. 아시다시피 추상화!

상속을 구성 및 위임 (및 일부 단점)으로 자동 대체하는 데 약간의 이점이 있지만이 과정에서 두뇌를 끄는 것은 큰 기회가 없습니다.

간접 지시는 매우 강력합니다. 현명하게 사용하십시오.


0

또 다른 이유는 다음과 같습니다. 많은 OO 언어 (여기서는 C ++, C # 또는 Java와 같은 언어를 생각하고 있습니다)에서 객체 클래스를 만든 후에 변경할 수있는 방법이 없습니다.

직원 데이터베이스를 작성한다고 가정하십시오. 우리는 추상 기본 직원 클래스를 보유하고 있으며 엔지니어, 경비원, 트럭 운전사 등 특정 역할에 대한 새로운 클래스를 도출하기 시작합니다. 각 클래스에는 해당 역할에 대한 특수한 동작이 있습니다. 모든 좋은.

그런 다음 어느 날 엔지니어가 엔지니어링 관리자로 승격됩니다. 기존 엔지니어를 다시 분류 할 수 없습니다. 대신 엔지니어링 관리자를 작성하고 엔지니어의 모든 데이터를 복사하고 데이터베이스에서 엔지니어를 삭제 한 다음 새 엔지니어링 관리자를 추가하는 기능을 작성해야합니다. 그리고 가능한 각 역할 변경에 대해 이러한 기능을 작성해야합니다.

더 나쁜 것은 트럭 운전사가 장기 병가를 겪고 경비원이 트럭 운전을 파트 타임으로 채우라고 제안한다고 가정 해보십시오. 이제는 새로운 경비원 및 트럭 운전사 클래스를 발명해야합니다.

구체적인 Employee 클래스를 작성하고 작업 역할과 같은 특성을 추가 할 수있는 컨테이너로 처리하는 것이 훨씬 쉽습니다.


또는 역할 목록에 대한 포인터를 사용하여 직원 클래스를 만들고 각 실제 작업이 역할을 확장합니다. 너무 많은 일을하지 않고도 상속을 아주 훌륭하고 현명하게 사용하도록 예를들 수 있습니다.
T. Sar

0

상속은 두 가지를 제공합니다.

1) 코드 재사용 : 구성을 통해 쉽게 달성 할 수 있습니다. 실제로 캡슐화를 더 잘 보존하므로 구성을 통해 더 잘 수행됩니다.

2) 다형성 : "인터페이스"(모든 방법이 순수 가상 인 클래스)를 통해 달성 할 수 있습니다.

비 인터페이스 상속을 사용하는 강력한 주장은 필요한 메소드 수가 많고 서브 클래스가 작은 서브 세트 만 변경하려고 할 때입니다. 그러나 일반적으로 "단일 책임" 원칙 을 구독하는 경우 인터페이스의 설치 공간이 매우 작아야 하므로 이러한 경우는 거의 없습니다.

모든 부모 클래스 메서드를 재정의 해야하는 코딩 스타일이 있다고해도 어쨌든 많은 수의 통과 메서드를 작성하지 않아도되므로 구성을 통해 코드를 재사용하고 캡슐화의 추가 이점을 얻으십시오. 또한 다른 메소드를 추가하여 수퍼 클래스를 확장하려면 코딩 스타일에 해당 메소드를 모든 서브 클래스에 추가해야합니다 (그리고 "인터페이스 분리" 를 위반할 가능성이 높습니다 ). 상속에서 여러 수준을 갖기 시작하면이 문제가 빠르게 커집니다.

마지막으로, 많은 언어에서 "컴포지션"기반 객체의 단위 테스트는 멤버 객체를 모의 / 테스트 / 더미 객체로 대체하여 "상속"기반 객체의 단위 테스트보다 훨씬 쉽습니다.


0

이것이 실제로 상속보다 구성을 선호하는 유일한 이유입니까?

아니.

상속보다 구성을 선호하는 데는 여러 가지 이유가 있습니다. 정답이 하나도 없습니다. 그러나 나는 상속보다 구성을 선호하는 또 다른 이유를 믹스에 넣을 것이다.

상속보다 컴포지션을 선호하면 코드의 가독성이 향상되므로 여러 사람과 함께 대규모 프로젝트를 수행 할 때 매우 중요합니다.

내가 생각하는 방법은 다음과 같습니다. 다른 사람의 코드를 읽을 때 클래스를 확장 한 것으로 보이면 수퍼 클래스에서 어떤 동작이 바뀌 었는지 물어볼 것입니다.

그런 다음 재정의 함수를 찾아 코드를 살펴 보겠습니다. 아무것도 보이지 않으면 상속의 오용으로 분류합니다. 5 분 동안 코드를 읽음으로써 저와 팀의 다른 모든 사람을 구하기 위해서만 대신 작문을 사용해야했습니다!

구체적인 예는 다음과 같습니다. Java에서 JFrame클래스는 창을 나타냅니다. 창을 만들려면 인스턴스를 JFrame만든 다음 해당 인스턴스에서 함수를 호출하여 물건을 추가하고 표시합니다.

많은 튜토리얼 (공식 튜토리얼조차도)은 JFrame클래스의 인스턴스를의 인스턴스로 취급 할 수 있도록 확장 할 것을 권장합니다 JFrame. 그러나 imho (그리고 다른 많은 사람들의 의견)는 이것이 들어가기에 꽤 나쁜 습관이라는 것입니다! 대신, 해당 인스턴스에서 인스턴스를 작성하고 JFrame함수를 호출해야합니다. JFrame부모 클래스의 기본 동작을 변경하지 않기 때문에이 인스턴스 를 확장 할 필요 는 없습니다.

반면에 사용자 정의 페인팅을 수행하는 한 가지 방법은 JPanel클래스 를 확장 하고 paintComponent()함수를 재정의하는 것입니다. 이것은 상속을 잘 사용하는 것입니다. 코드를 살펴보고 함수를 확장 JPanel하고 재정의 한 것을 확인하면 paintComponent()변경중인 동작을 정확하게 알 수 있습니다.

직접 코드를 작성하는 경우 컴포지션을 사용하는 것만 큼 상속을 사용하는 것이 쉬울 수 있습니다. 그러나 당신이 그룹 환경에 있고 다른 사람들이 당신의 코드를 읽는다고 가정하십시오. 상속보다 구성이 유리하면 코드를 더 쉽게 읽을 수 있습니다.


상속 대신 컴포지션을 사용할 수 있도록 객체의 인스턴스를 반환하는 단일 메서드로 전체 팩토리 클래스를 추가하면 실제로 코드를 더 읽기 쉽게 만들 수 없습니다 ... (예, 매번 새 인스턴스가 필요할 경우 필요합니다. 상속이 좋다는 의미는 아니지만 컴포지션이 항상 가독성을 향상시키는 것은 아닙니다.
jpmc26

1
@ jpmc26 ... 왜 지구상에서 클래스의 새 인스턴스를 만들기 위해 팩토리 클래스가 필요한가? 그리고 상속은 당신이 묘사 한 내용의 가독성을 어떻게 향상 시킵니까?
Kevin Workman

위의 의견에서 그러한 공장에 대한 이유를 명시 적으로주지 않았습니까? 읽을 코드가 적기 때문에 더 읽기 쉽습니다 . 기본 클래스와 일부 서브 클래스에서 단일 추상 메소드로 동일한 동작을 수행 할 수 있습니다. (어쨌든 각각에 대해 구성된 클래스를 다르게 구현하게 될 것입니다.) 객체 구성의 세부 사항이 기본 클래스와 밀접하게 연결되어 있으면 코드의 다른 곳에서 많이 사용되지 않으므로 추가로 줄어 듭니다. 별도의 수업을 만드는 것의 이점. 그런 다음 밀접하게 관련된 코드 부분을 서로 가깝게 유지합니다.
jpmc26

내가 말했듯이, 이것이 상속이 반드시 좋은 것을 의미하는 것은 아니지만, 어떤 상황에서 구성이 가독성을 향상시키지 않는 방법을 예시합니다.
jpmc26

1
구체적인 예를 보지 않으면 실제로 언급하기가 어렵습니다. 그러나 당신이 묘사 한 것은 나에게 과도하게 엔지니어링 된 것처럼 들립니다. 클래스의 인스턴스가 필요하십니까? new키워드는 당신에게 하나를 제공합니다. 어쨌든 토론 주셔서 감사합니다.
Kevin Workman
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.