티스토리 뷰

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

 

🔎 인터페이스 타입 선언 vs 클래스 타입 선언

앞서, 아이템 51에서 매개변수의 타입으로는 클래스보다 인터페이스가 더 낫다고 했다.

매개변수로 적합한 인터페이스가 있다면 이를 구현한 클래스가 아닌 그 인터페이스를 직접 사용한다.

 

여기서 더하여, 적합한 인터페이스만 있다면 매개변수뿐만 아니라 반환 값, 변수, 필드를 전부 인터페이스 타입으로 선언하는 것이 좋다.

객체의 실제 클래스를 사용해야 할 상황은 오직 생성자로 생성할 때뿐이다.

 

예를 들어, LinkedHashSet을 선언하는 두 가지 방법이 있다.

/* 좋은 예 : 인터페이스를 타입으로 사용 */
Set<Son> sonSet = new LinkedHashSet<>();

/* 나쁜 예 : 클래스를 타입으로 사용 */
LinkedHashSet<Son> sonSet = new LinkedHashSet<>();

이렇게 인터페이스를 타입으로 사용하면 나중에 구현 클래스를 교체하고자 할 때 새 클래스의 생성자를 호출해주기만 하면 된다. 

HashSet을 사용하고 싶다면 Set <Son> sonSet = new L=HashSet <>();으로 쉽게 변경할 수 있다.

 

📢 인터페이스 타입 선언을 할 때 주의할 점!

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

예를 들면, LinkedHashSet을 HashSet으로 바꾸는 경우가 있다.

LinkedHashSet은 반복자의 순회 순서를 보장하지만, HashSet은 그렇지 않다. 

따라서 원래 것보다 성능이 좋거나 멋진 신기능을 발견했다면 같은 기능을 제공하는지 확인 후 코드 한 줄만 변경하면 된다.

 

여기서, 클래스 타입 선언했을 때도 선언 타입과 구현 타입을 동시에 바꿀 수 있으니 코드 한 줄만 바꾸면 되잖아?라고 생각할 수 있다.

하지만 클라이언트에서 기존 타입에서만 제공하는 메서드를 사용했거나, 기존 타입을 사용해야 하는 다른 메서드에 그 인스턴스를 넘겼을 때, 새로운 코드에서는 컴파일조차 되지 않는 결과를 초래할 수 있다.

 

🔎 적합한 인터페이스가 없을 땐?

적합한 인터페이스가 없다면 당연히 클래스로 참조해야 한다.

1. String과 BigInteger 같은 값 클래스

값 클래스를 여러 가지로 구현될 수 있다고 생각하고 설계하는 일은 거의 없기 때문에 final인 경우가 많고, 상응하는 인터페이스가 별도로 존재하는 경우가 드물다.

이런 값 클래스는 매개변수, 변수, 필드, 반환 타입으로 사용해도 무방하다.

 

2. 클래스 기반으로 작성된 프레임워크가 제공하는 객체들

클래스 기반으로 작성된 프레임워크가 제공하는 객체들이라 할지라도, 특정 구현 클래스보다는 보통은 추상 클래스인 기반 클래스를 사용해 참조하는 것이 좋다.

OutputStream 등 java.io 패키지의 여러 클래스가 이 부류에 속한다.

 

3. 인터페이스에는 없는 특별한 메서드를 제공하는 클래스들

예를 들어, PriorityQueue 클래스는 Queue 인터페이스에는 없는 comparator 메서드를 제공한다.

클래스 타입을 직접 사용하는 경우는 이런 추가 메서드를 꼭 사용해야 하는 경우로 최소화해야 하며, 절대 남발하지 말자

 

이 세 가지 부류보다 더 많은 경우가 있을 수 있지만,

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

728x90
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/06   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함
250x250