11. 합성과 유연한 설계
- 상속 관계는
is-a 관계
라 부르고 합성 관계는has-a 관계
라 불립니다. - 합성은 구현에 의존하지 않다는 점에서 상속과 다릅니다. 합성은 내부에 포함되는 객체의 구현이 아닌 퍼블릭 인터페이스에 의존합니다.
#
01. 상속을 합성으로 변경하기#
불필요한 인터페이스 상속 문제- 상속이 초래하는 문제는 다음과 같습니다.
- 불필요한 인터페이스 상속 문제
- 메서드 오버라이딩의 오작용 문제
- 부모 클래스와 자식 클래스의 동시 수정 문제
#
java.util.Properties와 java.util.Stack- 합성 관계로 변경함으로써 클라이언트가 Stack을 잘못 사용할 수 있다는 가능성을 깔끔하게 제거합니다.
#
메서드 오버라이딩의 오작용 문제: InstrumentedHashSet- 내부의 인스턴스에게 동일한 메서드 호출을 그래도 전달하는 것을 포워딩(forwarding) 이라 부르고 동일한 메서드를 호출하기 위해 추가된 메서드를 포워드 메서드(forwarding method)라고 부릅니다.
#
부모 클래스와 자식 클래스의 동시 수정 문제: PersonalPlaylist- 대부분의 경우 구현에 대한 결합보다는 인터페이스 결합이 더 좋습니다.
#
02. 상속으로 인한 조합의 폭발적인 증가- 작은 기능들을 조합해서 더 큰 기능을 수행하는 객체를 만들어야 하는 경우, 두 문제점이 발생합니다.
- 하나의 기능을 추가하거나 수정하기 위해 불필요하게 많은 수의 클래스를 추가하거나 수정해야 합니다.
- 단일 상속만 지원하는 언어에서는 상속으로 인해 오히려 중복 코드의 양이 늘어날 수 있습니다.
#
기본 정책과 부가 정책 조합하기- 핸드폰 과금 시스템에 대한 정책을 가정했을 때, 기본 정책과 부가 정책으로 나눌 수 있습니다.
- 설계는 다양한 조합을 수용할 수 있도록 유연해야 합니다.
#
상속을 이용해서 기본 정책 구현하기- ...
#
기본 정책에서 세금 정책 조합하기- 유연성을 유지하면서도 중복 코드를 제거할 수 있는 방법은 메서드에 기본 구현을 함께 제공하는 것입니다. 이처럼 편의를 위해 기본 구현을 제공하는 메서드를 훅 메서드(Hook method) 라고 합니다.
- 대부분의 객체지향 언어는 단일 상속만 지원하므로 상속으로 인해 발생하는 중복 코드 문제를 해결하기는 쉽지 않습니다.
#
기본 정책에 기본 요금 할인 정책 조합하기- 중복 코드는 여러 문제를 이르킵니다.
#
중복 코드의 덫에 걸리다- 상속의 남용으로 하나의 기능을 추가하기 위해 필요 이상으로 많은 수의 클래스를 추가해야 하는 경우를 가리켜 클래스 폭발(class explosion) 문제라고 부릅니다.
- 클래스 폭발 문제는 자식 클래스가 부모 클래스의 구현에 강하게 결합되도록 강요하는 상속의 근본적인 한계 때문에 발생하며, 컴파일 타임에 결정된 자식 클래스와 부모 클래스 사이는 변경할 수 없으므로 유일한 해결 방법은 조합의 수만큼 새로운 클래스를 추가하는 것 뿐입니다.
- 이 문제를 해결할 수 잇는 최선의 방법은 상속을 포기하는 것입니다.
#
03. 합성 관계로 변경하기- 상속과 달리 합성 관계는 런타임에 동적으로 변경할 수 있습니다.
- 합성을 사용하면 구현 시점에 정책들의 관계를 고정시킬 피요가 없으며 실행 시점에 정책들의 관계를 유연하게 변경할 수 있게 됩니다.
#
기본 정책 합성하기- 다양한 종류의 객체와 협력하기 위해 합성 관계를 사용하는 경우에는 합성하는 객체의 타입을 인터페이스나 추상 클래스로 선언하고 의존성 주입을 사용해 런타임에 필요한 객체를 설정할 수 있도록 구현하는 것이 일반적입니다.
#
부가 정책 적용하기#
기본 정책와 부가 정책 합성하기- 다양한 방식으로 정책들을 조합할 수 있는 설계가 준비되어 있으며, 남은 일은 원하는 정책의 인스턴스를 생성한 후 의존성 주입을 통해 다른 정책의 인스턴스에 전달하는 것뿐입니다.
- 객체를 조합하고 사용하는 방식이 상속하는 사용한 방식보다 더 예측가능하고 일관성이 있다는 사실이 있습니다.
#
새로운 정책 추가하기- 중요한 것은 요구사항을 변경할 때 오직 하나의 클래스만 수정해도 됩니다.
#
객체 합성이 클래스 상속보다 더 좋은 방법이다.- 코드를 재사용하면서도 건전한 결합도를 유지할 수 있는 더 좋은 방법은 합성을 이용하는 것입니다.
- 믹스인이라는 이름으로 알려져 있는 방법은 상속과 합성의 특성을 모두 보유하고 있는 독특한 코드 재사용 방법입니다.
#
04. 믹스인- 믹스인(mixin) 은 객체를 생성할 때 코드 일부를 클래스 안에 섞어 넣어 재사용하는 기법을 가리키는 용어입니다.
- 상속이 클래스와 클래스 사이의 관계를 고정시키는 데 비해 믹스인은 유연하게 관계를 재구성할 수 있습니다.
#
기본 정책으로 구현하기#
트레이트로 부가 정책 구현하기- 상속은 정적이지만 믹스인은 동적입니다.
- 합성은 독립적으로 작성된 객체들을 실행 시점에 조합해서 더 큰 기능을 만들어내는 데 비해 믹스인은 독립적으로 작성된 트레이트와 클래스를 코드 작성 시점에 조합해서 더 큰 기능을 만들어낼 수 있습니다.
#
부가 정책 트레이트 믹스인하기- 스칼라는 트레이트를 클래스나 다른 트레이트에 믹스인 할 수 있도록
extend
와with
키워드를 제공합니다. - 믹스인하려는 대상 클래스의 부모 클래스가 존재하는 경우 부모 클래스는
extends
를 이용해 상속하고 트레이트는with
를 이용해 믹스인 하는 것을 트레이트 조합(trait composition) 이라고 합니다. - 스칼라는 특정 클래스에 믹스인한 클래스와 트레이트를 선형화(linearization) 해서 어떤 메서드를 호출할지 결정합니다.
#
쌓을 수 있는 변경- 믹스인은 상속 계층 안에서 확장한 클래스보다 더 하위에 위치하게 됩니다.
- 믹스인은 추상 서브클래스(abstract subclass) 라고 부릅니다.
- 믹스인을 사용하면 특정한 클래스에 대한 변경 또는 확장을 독립적으로 구현한 후 필요한 시점에 차례대로 추가할 수 있습니다.