출처 : 오브젝트 (조영호 저)
상속에서 부모 클래스와 자식 클래스 사이의 의존성은 컴파일타임에 해결되지만 합성에서는 런타임에 해결된다. 상속관계는 is-a 관계라고 부르고 합성관계는 has-a관계라고 부른다.
합성은 구현에 의존하지 않는다는 점에서 상속과 다르다. 내부에 포함되는 객체의 구현이 아닌 퍼블릭 인터페이스에 의존한다. 따라서 합성을 사용하면 포함된 객체의 내부 구현이 변경되더라도 영향을 최소화할 수 있기 때문에 변경에 더 안정적인 코드를 얻을 수 있게 된다.
코드 작성 시점에 결정한 상속관계는 변경이 불가능하지만 합성관계는 실행시점에 동적으로 변경할 수 있으므로 합성이 변경하기 쉽고 유연한 설계를 얻을 수 있다.
클래스 상속은 다른 클래스를 이용해서 한 클래스의 구현을 정의하며, 이처럼 서브 클래싱에 의한 재사용을 화이트박스 재사용(white-box reuse)라고 한다. 상속을 받으면 부모 클래스의 내부가 자식 클래스에 공개되기 때문에 화이트박스인 셈이다.
객체합성은 합성할 객체들의 인터페이스를 명확하게 정의해야만 한다. 이런 스타일의 재사용을 블랙박스 재사용(black-box reuse)라고 하는데 객체의 내부는 공개되지 않고 인터페이스를 통해서만 재사용되기 때문이다.
상속의 문제점
합성을 사용하면 상속이 초래하는 세 가지 문제점을 해결할 수 있다. 상속을 합성으로 바꾸는 방법은 자식 클래스에 선언된 상속관계를 제거하고 부모 클래스의 인스턴스를 자식 클래스의 인스턴스 변수로 선언하면 된다.
부모 클래스에 추상 메서드를 추가하면 오든 자식 클래스들이 추상 메서드를 오버라이딩해야 하는 문제가 발생한다. 자식 클래스의 수가 적다면 큰 문제가 아니지만 자식 클래스가 수가 많을 경우에는 꽤나 번거롭다.
추상 메서드와 훅 메서드
개방-폐쇄 원칙의 설계 중 한 가지 방법은 부모 클래스에 새로운 추상메서드를 추가하고 부모 클래스의 다른 메서드 안에서 호출하는 것이다. 자식 클래스는 추상 메서드를 오버라이딩하고 자신만의 로직을 구현해서 부모 클래스에서 정의한 플로우에 개입할 수 있다.
추상메서드의 단점은 모든 자식 클래스가 추상 메서드를 오버라이딩해야 하는 것이다. 이를 해결하기 위해 메서드에 기본 구현을 제공하는 것이다. 자식 클래스에서 오버라이딩할 의도로 메서드를 추가했지만 편의를 위해 기본 구현을 제공하는 메서드를 훅 메서드(hook method)라고 한다.
만약 기본 정책 외에 부가 정책이 계속 추가되는 상황에서 상속의 남용으로 하나의 기능을 추가하기 위해 필요 이상으로 많은 수의 클래스를 추가해야 하는 경우를 가리켜 클래스 폭발(class explosion)문제 또는 조합의 폭발(combinational explosion)문제라고 한다.
컴파일타임에 결정된 자식 클래스와 부모 클래스 사이의 관계는 변경될 수 없기 때문에 자식 클래스와 부모 클래스의 다양한 조합이 필요한 상황에서 유일한 해결 방법은 조합의 수만큼 새로운 클래스를 추가하는 것 뿐이다.
합성을 사용하면 구현 시점에 정책들의 관계를 고정시킬 필요가 없으며 실행 시점에 정책들의 관계를 유연하게 변경할 수 있게 된다. 합성은 조합을 구성하는 요소들을 개별 클래스로 구현한 후 실행 시점에 인스턴스를 조립하는 방법을 사용하는 것이라고 할 수 있다.
믹스인(mixin)은 객체를 생성할 때 코드 일부를 클래스안에 섞어 넣어 재사용하는 기법을 가리키는 용어이다. 합성이 실행 시점에 객체를 조합하는 재사용 방법이라면 믹스인은 컴파일 시점에 필요한 코드 조각을 조합하는 재사용 방법이다.
상속이 클래스와 클래스 사이의 관계를 고정시키는 데 비해 믹스인은 유연하게 관계를 재구성할 수 있다. 처음 믹스인을 접하면 개념을 이해하기가 다소 어려울 수도 있는데 코드를 섞어 넣는다는 기본 개념을 구현하는 방법이 언어마다 다르기 때문이다.