[Effective Java 3/e] 아이템 18 - 상속보다는 컴포지션을 사용하라
아이템 18 - 상속보다는 컴포지션을 사용하라
래퍼 클래스 - 상속 대신 컴포지션을 사용했다
public class InstrumentedSet<E> extends ForwardingSet<E> {
private int addCount = 0;
public InstrumentedSet(Set<E> s) {
super(s);
}
@Override public boolean add(E e) {
addCount++;
return super.add(e);
}
@Override public boolean addAll(Collection<? extends E> c) {
addCount += c.size();
return super.addAll(c);
}
public int getAddCount() {
return addCount;
}
}
재사용할 수 있는 전달 클래스
public class ForwardingSet<E> implements Set<E> {
private final Set<E> s;
public ForwardingSet(Set<E> s) { this.s = s; }
public void clear() { s.clear(); }
...
public boolean add(E e) { return s.add(e); }
public boolean addAll(Collection<? extends E> c) { return super.addAll(c); }
...
}
- InstrumentedSet은 HashSet의 모든 기능을 정의한 Set 인터페이스를 활용해 설계되어 견고하고 유연하다.
- 임의의 Set에 계측 기능을 덧씌워 새로운 Set으로 만드는 것이 이 클래스의 핵심
- 상속 방식은 구체 클래스 각각을 따로 확장해야 함
- 지원하고 싶은 상위 클래스의 생성자 각각에 대응하는 생성자를 별도로 정의해줘야 함
- 한 번만 구현해두면 어떠한 Set 구현체라도 계측 가능하면, 기존 생성자들과도 함께 사용 가능
- Set
times = new InstrumentedSet<>(new TreeSet<>(cmp)); - Set
s = new InstrumentedSet<>(new HashSet<>(INIT_CAPACITY));
- Set
정리
- 상속은 강력하지만 캡슐화를 해치는 문제가 있다.
- 상속은 클래스와 하위 클래스가 순수한 is-a 관계일 때만 써야 한다.
- is-a 관계이여도 하위 클래스의 패키지가 상위 클래스와 다르고, 상위 클래스가 확장을 고려해 설계되지 않았다면 문제가 될 수 있다.
- 상속 취약점을 피할려면 상속 대신 컴포지션과 전달을 사용하자.
- 래퍼 클래스로 구현할 인터페이스가 있다면 더욱 권장
- 래퍼 클래스는 하위 클래스보다 견고하고 강력