Java

[Effective Java] Item20. 추상 클래스보다는 인터페이스를 우선하라

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

 

자바는 단일 상속, 다중 구현 메커니즘을 가진다.

즉, 클래스는 하나만 상속할 수 있지만 인터페이스는 여러 개를 구현할 수 있다.

 

! 클래스, 인터페이스의 차이

클래스 : 추상 클래스를 상속받는 클래스는 반드시 추상 클래스의 하위 클래스가 된다.

인터페이스 : 어떤 클래스를 상속하든 하위 타입이 아닌 같은 타입으로 취급된다.

 

 

! 인터페이스는 믹스인(mixin) 정의에 안성맞춤이다

 

기존 클래스에 손쉽게 인터페이스를 구현할 수 있다.

반면, 기존 클래스에 추상 클래스를 끼워 넣기는 어렵다. 계층구조상 상위<->하위의 구조를 가지므로 클래스 계층구조에 혼란을 야기한다.

이러한 이유로, 인터페이스는 *믹스인(mixin) 정의에 안성맞춤이다.

 

*믹스인(mixin) : 대상 타입의 주된 기능에 선택적 기능을 혼합하는 것. 예를들어 Comparable은 자신을 구현한 클래스의 인스턴스끼리는 순서를 정할 수 있다

고 선택적 기능을 혼합하는 믹스인이다.

 

 

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

 

위 내용과 이어지는 내용이다.

클래스는 구체 클래스가 추상 클래스의 하위 계층 클래스가 되지만, 인터페이스는 같은 타입으로 취급된다.

 

가수(Singer)와 작곡가(Songwriter) 인터페이스가 있을 때, 작곡과 노래를 모두 하는 싱어송라이터(SingerSongwriter)는

Singer와 Songwriter 인터페이스를 모두 확장하여 새로운 인터페이스로 정의할 수 있다. (+ 새로운 메서드를 추가할 수도 있다)

 

// 가수
public interface Singer {
    void sing();
}

// 작곡가
public interface SongWriter {
    void compose();
}

// 싱어송라이터 (가수 + 작곡)
public interface SingerSongwriter extends Singer, SongWriter {
    // 새로운 메서드를 추가할 수도 있다
    void strum();
    void actSensitive();
}

 

! 인터페이스와 추상 골격 구현(skeletal implementation) 클래스를 함께 제공하여 인터페이스 + 추상클래스의 장점을 모두 취할 수 있다

 

인터페이스로는 타입을 정의하고, 골격 구현 클래스는 나머지 메서드를 구현함으로써 인터페이스를 구현할 때 공통된 부분을 추상클래스로 해결할 수 있다. 이를 템플릿 메서드 패턴이라 한다.

 

골격 구현 클래스를 직접 구현해보고, 이를 활용해보자.

 

Character라는 인터페이스가 존재한다. 해당 인터페이스는 생성(create), 사냥(hunt), 레벨업(levelUp)의 메서드를 가지며,

생성 - 사냥 - 레벨업을 순서대로 수행하는 process라는 메서드를 가진다.

 

public interface Character {
    void create();
    void hunt();
    void levelUp();
    void process();
}

 

Warrior와 Archer 클래스를 생성하여 위 Character 인터페이스를 구현해본다.

 

// Warrior
public class Warrior implements Character {

    @Override
    public void create() {
        System.out.println("create");
    }

    @Override
    public void hunt() {
        System.out.println("warrior hunt");
    }

    @Override
    public void levelUp() {
        System.out.println("levelup");
    }

    @Override
    public void process() {
        create();
        hunt();
        levelUp();
        System.out.println("============");
    }
}

 

// Archer
public class Archer implements Character {

    @Override
    public void create() {
        System.out.println("create");
    }

    @Override
    public void hunt() {
        System.out.println("archer hunt");
    }

    @Override
    public void levelUp() {
        System.out.println("levelup");
    }

    @Override
    public void process() {
        create();
        hunt();
        levelUp();
        System.out.println("============");
    }
}

 Warrior와 Archer 클래스를 자세히 보면, hunt 메서드를 제외하고는 모두 동일한 동작을 수행한다.

이럴 때 골격 구현 클래스를 구현하여 공통된 메서드는 골격 구현 클래스에서 구현하고, 공통되지 않은 메서드에 대해서만 상속 받은 클래스에서 구현하도록 할 수 있다.

 

골격 구현 클래스 AbstractCharacter를 구현한다.

* 관례상 골격 구현 클래스의 이름은 Abstract + 인터페이스 이름으로 짓는다.

 

// AbstractCharacter
public abstract class AbstractCharacter implements Character {
    @Override
    public void create() {
        System.out.println("Character create");
    }

    @Override
    public void levelUp() {
        System.out.println("Character levelUp");
    }

    @Override
    public void process() {
        create();
        hunt();
        levelUp();
        System.out.println("============");
    }
}

 

해당 골격 구현 클래스를 Warrior와 Archer 클래스에 적용한다.

 

public class Warrior extends AbstractCharacter {

    @Override
    public void hunt() {
        System.out.println("warrior hunt");
    }
}

 

public class Archer extends AbstractCharacter {

    @Override
    public void hunt() {
        System.out.println("archer hunt");
    }
}

 

public static void main(String[] args) {
    Warrior warrior = new Warrior();
    warrior.process();

    Archer archer = new Archer();
    archer.process();
}

/** log
Character create
warrior hunt
Character levelUp
============
Character create
archer hunt
Character levelUp
============
**/

 

Warrior와 Archer 클래스는 골격 구현 클래스에 의해 필요한 메서드(hunt)만 구현할 수 있게 되었다.

물론 골격 구현 클래스에서 구현한 메서드도 override하여 재정의 할 수 있다.

 

 

 

소스코드 : https://github.com/HunSeongPark/effective-java/tree/master/src/main/java/item20

 

GitHub - HunSeongPark/effective-java: 책 Effective Java 3/E 공부

책 Effective Java 3/E 공부. Contribute to HunSeongPark/effective-java development by creating an account on GitHub.

github.com