DEV ℧ Developer Diary

[EffectiveJava] item64 - 객체는 인터페이스를 사용해 참조하라

Item51에서 매개변수 타입을 클래스가 아니라 인터페이스를 사용하라고 했다. 이 의미는 “객체는 클래스가 아닌 인터페이스로 참조하라”고 까지 확장할 수 있다.

적합한 인터페이스만 있다면 매개변수뿐 아니라 반환값, 변수, 필드를 전부 인터페이스 타입으로 선언하라. 객체의 실제 클래스를 사용해야 할 상황은 ‘오직’ 생성자로 생성할 때 뿐이다.

인터페이스 참조

좋은 예. 인터페이스를 타입으로 사용

Set<Son> sonSet = new LinkedHashSet<>();

나쁜 예. 클래스를 타입으로 사용

LinkedHashSet<Son> sonSet = new LinkedHashSet<>();

인터페이스를 타입으로 사용하는 습관을 길러두면 프로그램이 훨씬 유연해질 것이다.

나중에 구현 클래스를 교체하고자 한다면 그저 새 클래스의 생성자를 호출해주기만 하면 된다. 다음과 같이 말이다.

Set<Son> sonSet = new HashSet<>();

주의할 점

원래의 클래스가 인터페이스의 일반 규약 이외의 특별한 기능을 제공하며, 주변 코드가 의존성을 가지고 있다면, 새로운 클래스도 반드시 같은 기능을 제공해야 한다.

LinkedHashSetSet내부의 순서를 보장하지만 HashSet은 보장하지 않는 차이와 같은 경우가 있다.

구현 클래스의 교체 이유

기존 구현한 구현체보다 성능이 좋거나 신기능을 제공 할 수 있기 때문에, 인터페이스 타입을 이용해 유연하게 작성되는 것이 좋다.

클래스 참조

하지만 인터페이스 타입이 아닌, 클래스로 참조를 해야하는 경우도 있다.

적합한 인터페이스가 없는 경우

적합한 인터페이스가 없다면 당연히 클래스로 참조해야 한다. StringBigInteger 같은 값 클래스가 그렇다. 값 클래스를 여러 가지로 구현될 수 있다고 생각하고 설계하는 일은 거의 없다.
이런 값 클래스는 매개변수, 변수, 필드, 반환 타입으로 사용해도 무관하다.

클래스 기반으로 작성된 프레임워크 제공 객체

클래스 기반으로 작성된 프레임워크가 제공하는 객체들의 경우 특정 구현 클래스보다는 (보통 추상 클래스이다) 기반 클래스를 사용해 참조하는 게 좋다. OutputStreamjava.io 패키지의 여러 클래스가 이 부류에 속한다.

특별한 메서드를 제공하는 클래스

인터페이스에는 없는 특별한 메서드를 제공하는 클래스인 경우에도 클래스 타입을 사용한다. PriorityQueue 클래스는 Queue 인터페이스에는 없는 comparator 메서드를 제공한다.
클래스 타입을 직접 사용하는 경우는 이런 추가 메서드를 꼭 사용해야 하는 경우로 최소화 해야 하며, 절대 남발하지 말아야 한다.

그외 더 다양한 상황이 있으나 모든 상황을 설명하기에는 무리가 있다.

주어진 객체를 표현할 적절한 인터페이스가 있는지 찾아서 그 인터페이스로 참조하면 더 유연하고 세련된 프로그램을 만들 수 있다.

적합한 인터페이스가 없다면 클래스의 계층구조 중 필요한 기능을 만족하는 가장 덜 구체적인(상위의) 클래스를 타입으로 사용하자.