Java-Bom / ReadingRecord

📚 책 읽고 정리하기 📚
https://javabom.tistory.com/category/Reading%20Record
474 stars 27 forks source link

[아이템 30] 시뮬레이트한 셀프 관용구 #75

Closed chldbtjd2272 closed 4 years ago

chldbtjd2272 commented 4 years ago

180p 시뮬레이트한 셀프관용구에 대한 설명이 나오는데, 이게 2장의 Builder패턴 pizza를 상속받은 nypizza 빌더패턴에 대한 이야기야, 이거 한번 구현해보고 실제로 왜 재귀적 타입한정을 써야 이런 구현들이 가능한지, 재귀적 타입한정을 안쓰면 왜 불가능한지에 대해 설명해줬으면 좋겠어

csbsjy commented 4 years ago

재귀적 타입 한정을 이용하는 제네릭타입을 사용하면 추상메서드 self 를 지원함으로써 하위클래스에서 형변환 하지 않고도 상위타입에서 구현한 메서드를 연쇄적으로 호출할 수 있는데,

public abstract class PayCard {
    final Set<Benefit> benefits;

    PayCard(Builder<?> builder) {
        benefits = builder.benefits.clone();
    }

    public enum Benefit {
        POINT("포인트"), SALE("할인"), SUPPORT("연회비지원");

        Benefit(String benefit) {
        }
    }

    abstract static class Builder<T extends Builder<T>> { // 재귀적 타입한정
        EnumSet<Benefit> benefits = EnumSet.noneOf(Benefit.class);

        public T addBenefit(Benefit benefit) {
            this.benefits.add(benefit);
            return self();
        }

        abstract PayCard build();

        protected abstract T self();
    }
}

이거 예전에 민형이가 자신의 메서드를 호출하는 메서드는 재정의가능하게 만들지 말라는거 설명했을 때 그 경우를 활용한 예? 같더라고 ,,? 상위타입의 addBenefit 메서드는 자기 자신의 self 메서드를 호출하고(추상메서드니까 괜찮지만) 하위타입에서는 상위타입의 addBenefit메서드를 호출하지만 self 메서드는 자기가 재구현한 self 메서드를 호출하는 것을 이용한거같아서 신기하더랑

이 추상클래스의 하위타입인 LottePayCard는 PayCard의 Builder를 상속한 Builder를 구현함으로써, 상위타입에서 말한 "Builder 를 상속하는 T"를 만족했고, 이렇게 함으로써 addBenefit에서 반환하는 self 를 오버라이딩 할 수 있게 돼. 즉, 상위 클래스의 addBenefit에서 self()를 호출할 때, 하위클래스인 LottePayCard가 구현한 self() 를 불러옴으르로써, 자기자신의 Builder 를 반환하게되고 그렇게 클라이언트에서는 상위클래스메서드든 하위클래스메서드든 신경쓰지 않고 메서드체이닝을 쓸 수 있는거얌.

public class LottePayCard extends PayCard {
    private final Sale sale;

    LottePayCard(Builder builder) {
        super(builder);
        sale = builder.sale;
    }

    public enum Sale {
        LOTTE_WORLD("롯데월드"), LOTTE_DEPT("롯데백화점");

        Sale(String sale) {
        }
    }

    public static class Builder extends PayCard.Builder<Builder> {
        private final Sale sale;

        public Builder(Sale sale) {
            this.sale = sale;
        }

        @Override
        LottePayCard build() {
            return new LottePayCard(this);
        }

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

글고 재귀적 타입한정을 안쓰면 불가능 하다기보다는 하위클래스를 구현할 때 self 메서드에 자기자신(Builder)를 반환하도록 강제하기 위한거 아닐까 ,,? 재귀적타입한정을 쓰지 않아도 제대로 PayCard.Builder 만 지켜주면 되긴 하니까는 ,, 어렵네욧

csbsjy commented 4 years ago
    @DisplayName("시뮬레이트한 셀프타입 관용구")
    @Test
    void simulate() {
        LottePayCard lottePayCard = new LottePayCard.Builder(LOTTE_DEPT)
                .addBenefit(PayCard.Benefit.POINT)
                .build();

    }
jyami-kim commented 4 years ago

엌ㅋㅋㅋㅋㅋ나랑 같은질문이었군,,, 낼 같이보고 지워용