Skip to content

Latest commit

 

History

History
64 lines (59 loc) · 3.08 KB

인터페이스는_구현하는_쪽을_생각해_설계하라_폰드.md

File metadata and controls

64 lines (59 loc) · 3.08 KB

인터페이스는 구현하는 쪽을 생각해 설계하라

자파 8 이전, 기존 구현체를 깨뜨리지 않고 인터페이스에 메서드를 추가할 방법이 없었다.
기존 인터페이스에 메서드를 추가할 수 있도록 디폴트 메서드 도입.
그러나 모든 기존 구현체들과 매끄럽게 연동되리라는 보장이 없다.

자바 8에서는 핵심 컬렉션 인터페이스들에 다수의 디폴트 메서드가 추가

default boolean removeIf(Predicate<? super E> filter) { //Collection 인터페이스에 추가된 디폴트 메서드
        Objects.requireNonNull(filter);
        boolean removed = false;
        final Iterator<E> each = iterator();
        while (each.hasNext()) {
            if (filter.test(each.next())) {
                each.remove();
                removed = true;
            }
        }
    return removed;
}

위 코드는 범용적으로 구현되었지만, 현존하는 모든 Collection 구현체와 잘 어울러지는 것은 아니다.
대표적인 예가 아파치의 SynchronizedCollection 클래스이다.

  • Collection 인터페이스 구현
  • 클라이언트가 제공한 객체로 락을 거는 능력 제공
  • 모든 메서드에서 주어진 락 객체로 동기화한 후, 내부 컬렉션 객체에 기능을 위임하는 래퍼 클래스
public class SynchronizedCollection<E> implements Collection<E>, Serializable {
    private static final long serialVersionUID = 2412805092710877986L;
    private final Collection<E> collection;
    protected final Object lock;
    ...

    protected SynchronizedCollection(Collection<E> collection) {
        this.collection = (Collection) Objects.requireNonNull(collection, "collection");
        this.lock = this;
    }
    ...

    public boolean add(E object) {
        synchronized (this.lock) {
            return this.decorated().add(object);
        }
    }
    ...
}

해당 클래스는 책이 쓰인 시점엔, removeIf 메서드를 재정의 하지 않았다.
removeIf 구현은 동기화에 관해 아무것도 고려되지 않았으므로, 해당 메서드의 구현을 물려받으면 모든 메서드 호출을 알아서 동기화한다는 약속이 깨짐.
→ 멀티 스레드 환경에서 해당 메서드 호출하면 예외를 발생하거나, 예기치 못한 결과가 생길 수 있다.

해결책

인터페이스의 디폴트 메서드를 재정의하고, 다른 메서드에서 디폴트 메서드를 호출하기 전에 필요한 작업(동기화 등..)을 수행하도록 변경

  public boolean removeIf(Predicate<? super E> filter) { //동기화 하도록 재정의
        synchronized(this.lock) {
            return this.decorated().removeIf(filter);
        }
    }

유의사항

  • 기존 인터페이스에 디폴트 메서드를 추가하는 것은 꼭 필요한 경우가 아니면 피해라.
    • 기존 구현체들과 충돌할 가능성을 고려해야 함.
  • 반면, 새로운 인터페이스를 만들 때, 표준적인 메서드 구현을 제공하는 데 디폴트 메서드는 유용한 수단.