이펙티브 자바 - item 18. 상속보다는 컴포지션을 이용하라

item 18. 상속보다는 컴포지션을 이용하라

이펙티브 자바 - item 18. 상속보다는 컴포지션을 이용하라

상속은 코드를 재사용하는 강력한 수단이지만 캡슐화를 깨뜨리고 커플링을 증가시키는 등의 여러 단점이 존재한다.

상위 클래스가 어떻게 구현되느냐에 따라 하위 클래스의 동작에 이상이 생길 수 있다. 또한 상위 클래스는 릴리스마다 내부 구현이 달라질 수 있으며, 그 여파로 코드 한 줄 건드리지 않은 하위 클래스가 오동작할 수 있다.

Composition을 사용하자

기존 클래스(InstrumentedHashSetWithComposition)를 확장하는 대신, 새로운 클래스(ForwardingSet)를 만들고 private 필드로 기존 클래스의 인스턴스(s)를 참조하게 하자

기존 클래스가 새로운 클래스의 구성요소로 쓰인다는 뜻에서 이러한 설계를 컴포지션(composition)이라 한다.

```java 기존 클래스 public class InstrumentedHashSetWithComposition extends ForwardingSet { private int addCount = 0;

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();
    }
}

InstrumentedHashSetWithCompositionHashSet의 모든 기능을 정의한 Set 인터페이스를 활용해 설계되어 견고하고 아주 유연하다.

구체적으로는 Set 인터페이스를 구현했고, Set의 인스턴스를 인수로 받는 생성자를 하나 제공한다.

임의의 Set에 계측 기능(getAddCount() 메서드)을 덧씌워 새로운 Set으로 만드는 것이 이 클래스의 핵심이다.