이펙티브 자바 - item 18. 상속보다는 컴포지션을 이용하라
on JAVA
이펙티브 자바 - item 18. 상속보다는 컴포지션을 이용하라
상속은 코드를 재사용하는 강력한 수단이지만 캡슐화를 깨뜨리고 커플링을 증가시키는 등의 여러 단점이 존재한다.
상위 클래스가 어떻게 구현되느냐에 따라 하위 클래스의 동작에 이상이 생길 수 있다. 또한 상위 클래스는 릴리스마다 내부 구현이 달라질 수 있으며, 그 여파로 코드 한 줄 건드리지 않은 하위 클래스가 오동작할 수 있다.
Composition을 사용하자
기존 클래스(InstrumentedHashSetWithComposition)를 확장하는 대신, 새로운 클래스(ForwardingSet)를 만들고 private 필드로 기존 클래스의 인스턴스(s)를 참조하게 하자
기존 클래스가 새로운 클래스의 구성요소로 쓰인다는 뜻에서 이러한 설계를 컴포지션(composition)이라 한다.
```java 기존 클래스 public class InstrumentedHashSetWithComposition
public InstrumentedHashSetWithComposition(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;
}
}
아래와 같이 전달 클래스를 만들어 컴포지션을 구현할 수 있다.
```java 새 클래스
// 전달 클래스
public class ForwardingSet<E> implements Set<E> {
private final Set<E> s;
//set 인스턴스를 인수로 받는 생성자
public ForwardingSet(Set<E> s) {
this.s = s;
}
public void clear() {
s.clear();
}
public boolean contains(Object o) {
return s.contains(o);
}
public boolean isEmpty() {
return s.isEmpty();
}
public int size() {
return s.size();
}
public Iterator<E> iterator() {
return s.iterator();
}
public boolean add(E e) {
return s.add(e);
}
public boolean remove(Object o) {
return s.remove(o);
}
public boolean containsAll(Collection<?> c) {
return s.containsAll(c);
}
public boolean addAll(Collection<? extends E> c) {
return s.addAll(c);
}
public boolean removeAll(Collection<?> c) {
return s.removeAll(c);
}
public boolean retainAll(Collection<?> c) {
return s.retainAll(c);
}
public Object[] toArray() {
return s.toArray();
}
public <T> T[] toArray(T[] a) {
return s.toArray(a);
}
@Override
public boolean equals(Object o) {
return s.equals(o);
}
@Override
public int hashCode() {
return s.hashCode();
}
@Override
public String toString() {
return s.toString();
}
}
InstrumentedHashSetWithComposition
은 HashSet
의 모든 기능을 정의한 Set 인터페이스를 활용해 설계되어 견고하고 아주 유연하다.
구체적으로는 Set 인터페이스를 구현했고, Set의 인스턴스를 인수로 받는 생성자를 하나 제공한다.
임의의 Set에 계측 기능(getAddCount() 메서드)을 덧씌워 새로운 Set으로 만드는 것이 이 클래스의 핵심이다.