아이템20. 추상 클래스보다는 인터페이스를 우선하라

아이템20. 추상 클래스보다는 인터페이스를 우선하라

728x90

자바가 제공하는 다중 구현 매커니즘은 인터페이스와 추상 클래스 이렇게 두가지다.

차이점

추상 클래스가 정의한 타입을 구현하는 클래스는 반드시 추상 클래스의 하위 클래스가 되어야 한다는 점이다. 자바는 단일 상속만 지원하니, 추상 클래스 방식은 새로운 타입을 정의하는 데 커다란 제약을 만든다.

반면 인터페이스가 선언한 메서드를 모두 정의하고 그 일반 규약을 잘 지킨 클래스라면 다른 어떤 클래스를 상속했든 같은 타입으로 취급된다.

기존 클래스에도 손쉽게 새로운 인터페이스를 구현해 넣을 수 있다.

인터페이스가 요구하는 메서드를 추가하고, 클래스 선언에 implements 구문만 추가하면 끝이다.

반면, 기존 클래스 위에 새로운 추상 클래스를 끼워 넣기는 어려운게 일반적이다. 두 클래스가 같은 추상 클래스를 확장하길 원한다면, 그 추상 클래스는 계층 구조상 두 클래스의 공통 조상이어야 한다. 이 방식은 계층 구조에 커다란 혼란을 준다. 새로 추가된 추상 클래스의 모든 자손이 이를 상속하게 되는 것이다. 그렇게 하는것이 적절하지 않는 상황에서도 강제로 말이다.

인터페이스로는 계층구조가 없는 타입 프레임워크를 만들 수 있다.

타입을 계층적으로 정의하면 수많은 개념을 구조적으로 잘 표현할 수 있지만, 현실에는 계층을 엄격히 구분하기 어려운 개념도 있다.

ex) 가수(Singer) , 작곡가(Songwriter)

public interface Singer { AudioClip sing(Song s); } public interface Songwriter { Song compose(int chartPosition); }

가수 클래스가 Singer와 Songwriter 모두를 구현해도 전혀 문제되지 않는다. 심지어 Singer와 Songwriter 모두를 확장하고 새로운 메서드까지 추가한 제 3의 인터페이스를 정의할 수도 있다.

public interface SingerSongwriter extends Singer, Songwriter{ AudioClip strum(); void actSensitive(); }

같은 구조를 클래스로 만들려면 가능한 조합 전부를 각각의 클래스로 정의한 고도비만 계층구조가 만들어 질것이다.

이를 추상클래스로 만들려고 하면 아래처럼 됨 (다중상속이 되지 않아 조합 폭발)

public abstract class Singer { abstract void sing(String s); } public abstract class SongWriter { abstract void compose(int chartPosition); } public abstract class SingerSongWriter { abstract void strum(); abstract void actSensitive(); abstract void Compose(int chartPosition); abstract void sing(String s); }

작곡을 할 수 있는 가수, 없는 가수 클래스를 각각 나눠서 작성하게 되면 클래스가 너무 많아짐. 속성이 n개라면 2^n개 만큼 클래스를 나눠야함 → 이를 조합 폭발(combination explosion)이라 부름

인터페이스를 사용하지 않으면 공통 기능을 정의한 타입이 없어서 매개변수 타입만 다른 메소드들을 엄청나게 많이 가지는 뚱뚱한 클래스가 나올 수 있음

public class ForwardingSet implements Set { private final Set s; public ForwardingSet(Set s) { this.s = s; } public boolean add(E e) { return s.add(e); } public boolean addAll(Collection c) { return s.addAll(c); } ..... }

인터페이스 메서드 중 구현 방법이 명확한 것이 있다면, default 메서드로 제공 가능함

default 메서드를 제공할 때 상속하는 사람들을 위한 설명을 @implSpec 자바독 태그를 붙여 문서화해야함.

equals나 hashCode 같은 Object 메서드를 디폴트 메서드로 제공해서는 안됨.

인터페이스는 인스턴스 필드를 가질 수 없고, public이 아닌 static 멤버도 가질 수 없음(자바 9부터는 private static 메서드도 구현 가능)

interface MyInterface { default void printHello() { System.out.println("Hello World"); } }

인터페이스와 추상 골격 구현 (skeletal implementation) 클래스를 함께 제공하는 식으로 인터페이스와 추상 클래스의 장점을 모두 취할 수 있음

인터페이스로 타입 정의, 필요하면 디폴트 메서드도 제공 골격 구현 추상 클래스가 나머지 메서드를 구현 관례 상, Abstract를 접두사로 붙임 (e.g. AbstractItem)

이렇게 해두면 단순히 골격 구현을 확장하는 것만으로 이 인터페이스를 구현하는 데 필요한 일이 대부분 완료

이러한 구현 방법을 템플릿 메소드 패턴(template method pattern)이라 함

요약

다중 구현용 타입으로는 인터페이스가 적합

복잡한 인터페이스라면 템플릿 메소드 패턴을 사용 고려

728x90

from http://development-record.tistory.com/101 by ccl(A) rewrite - 2021-11-21 22:27:28