///
Search
💡

item 21. 인터페이스는 구현하는 쪽을 생각해 설계하라 Design interfaces for posterity

디폴트 메서드가 있더라도

디폴트 메서드를 추가하는 데는 숙고가 필요하다. 인터페이스를 구현한 후 나중에 디폴트 메서드를 추가하면, 기존 구현체들과 연동될 것이라는 보장이 없기 때문이다.
일례로 Collections의 removeIf()가 있다. 아파치의 SynchronizedCollection 클래스는 모든 메서드를 동기화하여 호출해주는 역할을 하는데, removeIf()를 재정의하고 있지 않다. 이 상황에서 removeIf()를 호출하면, 해당 구현체가 모든 메서드에서 동기화 해주던 역할이 보장되지 못 한다.
default boolean removeIf(Predicate<? super E> filter) { 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; }
Java
복사
java 17의 Collection API
// https://mvnrepository.com/artifact/org.apache.commons/commons-collections4/4.4 public class SynchronizedCollection<E> implements Collection<E>, Serializable { /** Serialization version */ private static final long serialVersionUID = 2412805092710877986L; /** The collection to decorate */ private final Collection<E> collection; /** The object to lock on, needed for List/SortedSet views */ protected final Object lock; ... @Override public boolean remove(final Object object) { synchronized (lock) { return decorated().remove(object); } } /** * @since 4.4 */ @Override public boolean removeIf(final Predicate<? super E> filter) { synchronized (lock) { return decorated().removeIf(filter); } } ... }
Java
복사
Apache Commons Collection4 , 책 내용과는 달리 현재는 removeIf 를 오버라이드 해 두었다.

새로운 인터페이스를 만들 때

기존 인터페이스에 디폴트 메서드를 추가하는 경우와 달리, 새로운 인터페이스를 만들 땐 표준적인 메서드 구현을 제공하는 유용한 수단이며, 인터페이스를 더 쉽게 구현할 수 있도록 해준다.

오류를 줄이기 위한 제언

새로운 인터페이스라면 릴리스 전에 서로 다른 방식으로 최소한 세 가지는 구현하여 테스트 해봐야 한다. 또 각 인터페이스의 인스턴스를 다양한 작업에 활용하는 클라이언트도 여러 개 만들어 봐야 한다.
이렇게 해야 인터페이스를 릴리스 하기 전, 바로잡을 기회를 가질 수 있다. 인터페이스를 릴리스 한 후 결함을 수정하는 게 가능한 경우도 있겠지만, 절대 그 가능성에 기대서는 안 된다.

용어정리

한글명
영어명
후손
posterity