Effective Java Ch2. Item 2 추가 설명

Donghyeok Jang·2021년 8월 20일
0

Effective Java

목록 보기
3/4

해결책 3. 빌더 패턴 응용

지난 포스트에서 다룬 빌더를 실습하며 빌더 패턴의 구조를 파악해봤다.

빌더 패턴 중에 추상 클래스가 추상 빌더를 가지고 하위 클래스에서 추상 클래스의 상속을 받으며 각 하위 클래스용 추상 빌더를 상속받아 만들 수 있다고 하는 부분을 직접 구현하고 장점을 알아보았다.

다음은 간단하게 구상한 클래스 목록이다. 모든 필드는 빌드로 추가할 수 있어야한다.

  • 추상 클래스 - Movie Class
    + enum EQUIP { CAMERA, MIC, JIMMYJIP, PHONE ... }
    # Set<EQUIP> equips

  • 하위 클래스1 - ActionMovie Class
    - boolean stuntMan
    - boolean blood // required

  • 하위 클래스2 - HorroMovie Class
    + enum CHARACTER { ZOMBIE, DRACULA, GHOST, VAMPIRE ... }
    - int ageLimit

Abstract Movie Class

public class Movie {
    public enum EQUIP {
        CAMERA, MIC, JIMMYJIP, PHONE
    }

    protected final Set<EQUIP> equips;

    abstract static class Builder<T extends Builder<T>> { // 재귀적인 타입 매개변수
        EnumSet<EQUIP> equips = EnumSet.noneOf(EQUIP.class);

        public T addEquip(EQUIP equip){
            equips.add(Objects.requireNonNull(equip)); // 예외 처리
            
            // self() 메소드를 사용함으로써 하위 클래스의 객체를 반환해 하위 클래스에 있는 메소드를 사용할 수 있도록 한다.
            // 현재 Builder를 반환하면 하위 클래스에 있는 .ageLimit() 같은 메소드를 추가하지 못하기 때문
            return self(); 
        }

        abstract Movie build(); // 추상 메소드로서 하위 클래스에서 재정의 하도록 한다.

        protected abstract T self(); // JS에서 처럼 "self-type" 개념을 사용해서 메소드 체이닝이 가능하게 함

    }

    public Movie(Builder builder) {
        equips = builder.equips.clone();
    }
}

Action Movie Class

public class ActionMovie extends Movie{

    private final boolean stuntMan;
    private final boolean blood;

    public static class Builder extends Movie.Builder<Builder> {
        private boolean stuntMan = false;
        private boolean blood = false;

        public Builder(boolean blood) {
            this.blood = blood;
        }

        public Builder addStuntMan() {
            stuntMan = true;
            return this;
        }

        @Override
        public ActionMovie build() {
        	// ActionMovie 객체를 만들어 반환함으로 사용자가 타입 캐스팅을 하지 않아도 된다.
            return new ActionMovie(this);
        }

        @Override
        protected Builder self() {
            return this;
        }
    }

    public ActionMovie(Builder builder){
        super(builder);  // 상위 클래스 생성자 호출 -> equip 설정
        stuntMan = builder.stuntMan;
        blood = builder.blood;
    }

}

Horror Movie Class

public class HorrorMovie extends Movie {
    public enum CHARACTER {
        ZOMBIE, DRACULA, GHOST, WEREWOLF, VAMPIRE
    }

    private final Set<CHARACTER> characters;
    private final int ageLimit;

    public static class Builder extends Movie.Builder<Builder> {
        private EnumSet<CHARACTER> chracters = EnumSet.noneOf(CHARACTER.class);
        private int ageLimit;

        public Builder addCharacter(CHARACTER character){
        	// 필수 필드는 생성자의 매개변수에 추가해 필수로 지정할 수 있도록 한다.
            chracters.add(Objects.requireNonNull(character));
            return self();
        }

        public Builder ageLimit(int age){
            this.ageLimit = age;
            return this;
        }

        @Override
        HorrorMovie build() {
    	    // HorrorMovie 객체를 만들어 반환함으로 사용자가 타입 캐스팅을 하지 않아도 된다.
            return new HorrorMovie(this);
        }

        @Override
        public Builder self(){
            return this;
        }
    }

    public HorrorMovie(Builder builder){
        super(builder);
        this.characters = builder.chracters.clone();
        this.ageLimit = builder.ageLimit;
    }
}

Movie Client

public class MovieClient {
    ActionMovie actionMovie = new ActionMovie.Builder(true)
            .addEquip(Movie.EQUIP.CAMERA)
            .addEquip(Movie.EQUIP.MIC)
            .addStuntMan()
            .build();

    HorrorMovie horrorMovie = new HorrorMovie.Builder()
            .addEquip(Movie.EQUIP.CAMERA)
            .addEquip(Movie.EQUIP.ZIPLINE)
            .addEquip(Movie.EQUIP.MIC)
            .addCharacter(HorrorMovie.CHARACTER.DRACULA)
            .addCharacter(HorrorMovie.CHARACTER.VAMPIRE)
            .ageLimit(19)
            .build();
}

위 처럼 매개변수가 많더라도 사용하기 편하고 가독성도 좋은 코드를 작성할 수 있다. 또한 필드 추가,제거와 같이 확장성 있는 개발을 할 수 있다.

Ref.

Effective Java 3rd Edition by Joshua Bloch
백기선님 유튜브 동영상 및 자료
https://www.youtube.com/watch?v=X7RXP6EI-5E&list=PLOFN6hDJLxo0MYZd1z6GCaRdFWX3kTb6A&index=2

profile
Feedback is a gift

0개의 댓글