2011.04.18 20:20
 상속은 캡슐화를 위배하기 떄문에 부실한 소프트웨어를 초래한다.(textbook에서 다루는 상속의 문제점은 구현 상속(implementation inheritance, 클래스간의 상속)에서의 문제점을 의미한다. 인터페이스 상속(interface inheritance, 클래스에서 인터페이스를 구현하거나, 인터페이스가 다른 인터페이스로 확장되는 것)에는 적용되지 않는다.)

 textbook에서 거론한 잘못된 상속 사용법은 우리가 쉽게 저지를 수 있는, 그리고 이미 저지르고 있는 방법이므로 꼭 읽어보기를 바란다.


 그렇다면 대안은 무엇이 있을까?

 상속을 하는 대신 기존 클래스(상속에서 수퍼 클래스에 해당되는)의 인스턴스를 참조하는 private 필드를 새로운 클래스(상속에서 서브 클래스에 해당되는)에 둔다. 이런 식의 설계를 컴포지션(composition)이라 하는데, 그 이유는 기존 클래스가 새 클래스의 컴포넌트로 포함되기 때문이다.

  새 클래스의 각 인스턴스 메소드에서는 포함된 기존 클래스 인스턴스의 대응되는 메소드를 호출하여 결과를 반환할 수 있다. 이것을 포워딩(forwarding)이라 하고 새 클래스의 메소드를 포워딩 메소드라 한다. 이렇게 하면, 새 클래스는 기존 클래스의 내부 구현에 종속되지 않고 강건하게 될 것이다. 기존 클래스에 새로운 매소드를 추가하더라도 새 클래스에는 영향을 주지 않는다.

 다음의 예시 코드를 보면 이해하기 쉬울 것이다. 다음 예시 코드는 두부분으로 나누어져있다. InstrumentedHashSet 클래스 코드 및 재사용 가능한 포워딩 클래스(포워딩 메소드만 갖고 있는) 코드이다.

 이러한 InstrumentedSet 클래스를 래퍼(Wrapper) 클래스라고 하는데, 그 이유는 각 InstrumentedSet 인스턴스가 다른 Set 인스턴스를 포장하듯 포함하기 때문이다. 
 이것을 데코레이터 패턴(Decorator Pattern)이라고도 하는데, InstrumentedSet 클래스가 자신의 기능을 다른 Set에 덧붙여 치장("decorates")하기 때문이다. 컴포지션과 포워딩을 결합한 것을 막연하게 위임(delegation)이라고 하는 경우도 있지만, 래퍼 객체 참조가 자신이 포함된 객체에 전달되지 않는다면 기술적으로 위임이라고 할 수 없다.
 
 래퍼 클래스의 단점은 거의 없지만, 차후에 호출("콜백")이 될 수 있도록 객체 자신의 참조를 다른 객체에게 전달하는 콜백 프레임워크(Callback framework)에서의 사용은 부적합하다. 래퍼 객체에 포함된 객체는 자신의 래퍼 객체를 알지 못하므로, 콜백을 할 경우 자신의 참조(this)만을 다른 객체에게 전달하기 때문이다. 이것은 이미 SELF 문제라고 알려져있다.

 포워딩 메소드는 작성하기 번거롭지만, 각 인터페이스에 대해 포워딩 클래스는 한번만 작성하면 되며, 관련 인터페이스가 포함된 곳과 같은 패키지에 둘 수도 있을 것이다. 

 
 컴포지션 대신 상속을 사용하기로 결정할 때는 API의 결함이 없는 super 클래스와 진정한 서브 타입 관계 즉 "is-a"관계일 때만 사용하여야 하며, 그렇지 않다면 컴포지션을 사용해야 한다.
신고
Posted by JAVA_HOME

댓글을 달아 주세요


티스토리 툴바