가급적 상속(inheritance) 보다는 컴포지션(composition)을 사용하자. |
-
상속은 코드를 재사용하는 강력한 방법이다.
그러나 일을 하는데 가장 좋은 도구는 아니다.
잘못 사용하면 부실한 소프트웨어를 초래한다.
-
동일 프로그래머가 서브 클래스와 수퍼 클래스의 구현을 관장하는 같은 패키지 내에서 상속을 사용하는 것은 안전하다
상속을 위해 특별히 설계되고 문서화된 클래스를 확장(extends) 하기 위해 상속을 사용하는 것도 안전하다.
그러나 다른 패키지에 걸쳐 일반적인 실체 클래스로부터 상속을 받는 것은 위험하다.
-
상속은 캡슐화(encapsulation) 을 위배한다.
올바른 동작을 위해 서브 클래스는 자신의 수퍼 클래스가 구현하는 상세 내역에 의존한다.
수퍼 클래스의 구현 내역은 소프트웨어 배포판이 바뀌면서 변경될 수 있다.
따라서 서브 클래스는 수퍼 클래스에 맞춰 진화해야 한다.
-
수퍼 클래스의 기존 메소드를 오버라이딩 하지 않고 새 메소드를 추가할 때는 안전하다고 생각할 수 있으나,
꼭 그렇지는 않다.
추후 배포판의 수퍼 클래스에서 새 메소드를 추가할 때 그 메소드와 시그너처는 동일하고 반환 타입이 다른 메소드가 서브 클래스에 이미 있다면 메소드 오버라이딩을 잘못한 것으로 간주하여 컴파일 에러가 생긴다.
슈퍼 클래스와 동일한 시그니처 및 반환 타입을 갖고 있다면, 의도하지 않은 오버라이딩을 한 셈이 되어 또 다른 문제가 발생한다.
-
클래스를 확장(상속)하는 대신 기존 클래스의 인스턴스를 참조하는 private 필드를 새로운 클래스에 두는 컴포지션(composition) 으로 상속의 문제점들을 해결할 수 있다.
-
새 클래스의 각 인스턴스 메소드에서는 포함된 기존 클래스 인스턴스의 대응되는 메소드를 호출하여 결과를 반환할 수 있다. ( 포워딩(forwarding) )
새 클래스는 기존 클래스의 내부 구현에 종속되지 않으며, 기존 클래스에 새로운 메소드를 추가하더라도 새 클래스에는 영향을 주지 않는다.
-
컴포지트를 이용하여 모든 public 함수들을 forwarding 한 클래스를 래퍼(wrapper) 클래스라고 하는데,
이것을 데코레이터 패턴(decorator pattern) 이라고도 한다. ( 기존 클래스에 덧붙여 치장(decorate)하기 때문 )
래퍼 클래스의 단점은 거의 없지만, 컴포지트 된 객체 자신의 참조를 다른 객체에게 전달하는 콜백 프레임워크(callback framework)에서의 사용은 부적합하다.
래퍼 객체에 포함된 객체는 자신의 래퍼 객체를 알지 못하기 때문이다.
-
만약 상속을 꼭 사용하고 싶다면, 서브 클래스가 수퍼 클래스의 진정한 서브타입(subtype)인 경우에만 상속을 사용하는 것이 좋다.
즉 두 클래스가 "is-a" 관계일 때만 상속해야 한다.
만일 클래스 B를 클래스 A의 서브 클래스로 만들고 싶다면 다음 질문을 생각하라.
"모든 B객체가 진정한 A 인가?"
자신있게 "yes" 를 하지 못한다면 컴포지션을 고려해야 한다.
-
컴포지션 대신 상속을 사용하기로 결정하기 전에 최종으로 생각해봐야 할 질문들이 있다.
상속을 하려고 생각하는 클래스가 API 결함이 없는가?
만일 결함이 있으면 그 결함을 서브 클래스의 API 에 그대로 상속받을 것인가?
Summary
상속은 강력하지만 캡슐화를 위배하므로 문제가 된다.
서브 클래스와 수퍼 클래스간에 진정한 서브 타입 관계가 있을 때만 적합하다.
서브 클래스가 수퍼 클래스와 다른 패키지에 있고, 수퍼 클래스가 상속을 위해 설계된 것이 아니라면 상속은 서브 클래스를 망칠 수 있다.
상속 대신 컴포지션과 포워딩을 사용하자.
래퍼 클래스를 구현하는데 적합한 인터페이스가 이미 존재한다면 더욱 더 컴포지션을 사용하자.
서브 클래스보다 래퍼 클래스가 더욱 튼튼하고 강력하다.
'프로그래밍 놀이터 > 디자인 패턴, 리펙토링' 카테고리의 다른 글
[Effective Java] 추상 클래스보다는 인터페이스를 사용하자. (0) | 2016.11.07 |
---|---|
[Effective Java] 상속을 위한 설계와 문서화를 하자. 그렇지 않다면 상속의 사용을 금지시킨다. (0) | 2016.11.01 |
[Effective Java] 가변성을 최소화 하자. (0) | 2016.10.24 |
[Effective Java] public 클래스에서는 public 필드가 아닌 접근자(accessor) 메소드를 사용한다. (0) | 2016.10.21 |
[Effective Java] 클래스와 그 멤버의 접근성을 최소화하자. (0) | 2016.10.17 |
댓글