open:decorator-pattern

Decorator Pattern

포드래 베스퍼(Podrea Vesper)는 시합에서 우리가 속임수를 사용한 것을 적발했다. 우리에게는 하나의 선택권이 있다: 경찰에 체포되거나 그의 슈퍼 기사를 시합에 넣어주는 것이다.

페드로: 난 감옥에 가고 싶지 않아요.

이브: 저도요.

페드로: 그를 위해 한번 더 해보죠.

이브: 같은 방식이죠?

페드로: 비슷하지만, 똑같지는 않아요. 코만도는 군인이어서 시합에 참여하는 것이 허용되지 않죠. 그래서 우리가 맞추어 넣은 거구요. 하지만 기사는 시합 참여가 가능하니, 맞출 필요가 없구요. 기존 것에 기능을 추가하면 되요.

이브: 상속이나 합성?

페드로: 합성, 런타임에 행위를 바꾸는 데코레이터 패턴의 주요 목적이죠.

이브: 그러면 이 슈퍼 기사를 어떻게 할까요?

페드로: 그들은 Galahad 기사를 사용해서 더 많은 HP와 강력 갑옷을 장식할 계획이에요.

이브: 헤, 경찰이 게임을 하다니 재밌네요.

페드로: 네, 기사를 추상 클래스로 만듭시다.

public class Knight {
    protected int hp;
    private Knight decorated;

    public Knight() { }

    public Knight(Knight decorated) {
        this.decorated = decorated;
    }

    public void attackWithSword() {
        if (decorated != null) decorated.attackWithSword();
    }

    public void attackWithBow() {
        if (decorated != null) decorated.attackWithBow();
    }

    public void blockWithShield() {
        if (decorated != null) decorated.blockWithShield();
    }
}

이브: 그러면 저기서 무엇을 개선해야 되죠?

페드로: 인터페이스 대신에 클래스 Knight를 만들어서 hp(hit point) 멤버변수에 접근해요. 그러고 나서 2개의 생성자를 만드는데, 하나는 표준 행위를 위한 디폴트이고, 다른 하나는 데코레이트된 객체에 호출을 위임하는 데코레이트된 생성자이죠.

이브: 인터페이스 대신 추상 클래스를 쓰는 게 맞아요?

페드로: 아니요, 하지만 비슷한 행위를 하는 2개의 클래스를 피하고, 각 데코레이트된 객체를 기본 구현으로 채우는 거죠. 각 메소드의 구현을 강제하는 대신.

이브: 오케이, 특수 갑옷은요?

페드로: 역시 쉬워요.

public class KnightWithPowerArmor extends Knight {
    public KnightWithPowerArmor(Knight decorated) {
        super(decorated);
    }

    @Override
    public void blockWithShield() {
        super.blockWithShield();
        Armor armor = new PowerArmor();
        armor.block();
    }
}

public class KnightWithAdditionalHP extends Knight {
    public KnightWithAdditionalHP(Knight decorated) {
        super(decorated);
        this.hp += 50;
    }
}

페드로: 경찰의 요구 사항을 충족하는 2개의 데코레이터에요. 이제 슈퍼 기사를 만들 수 있어요. 이 슈퍼 기사는 Galahad와 같은 동작을 하지만, 특수 갑옷을 입고 있고, hp 점수도 50점 더 높아요.

Knight superKnight =
     new KnightWithAdditionalHP(
     new KnightWithPowerArmor(
     new Galahad()));

이브: 괜찮은 트릭이네요.

페드로: 자 이제 클로저로 비슷한 행위를 보여줘 보시죠.

이브: 여기요.

(def galahad {:name "Galahad"
              :speed 1.0
              :hp 100
              :attack-bow-fn attack-with-bow
              :attack-sword-fn attack-with-sword
              :block-fn block-with-shield})

(defn make-knight-with-more-hp [knight]
  (update-in knight [:hp] + 50))

(defn make-knight-with-power-armor [knight]
  (update-in knight [:block-fn]
             (fn [block-fn]
               (fn []
                 (block-fn)
                 (block-with-power-armor)))))

;; create the knight
(def superknight (-> galahad
                     make-knight-with-power-armor
                     make-knight-with-more-hp)

페드로: 똑같은 기능이네요.

이브: 네, 다만 특수 갑옷 데코레이터(make-knight-with-power-armor)만 주의해서 보세요.


  • open/decorator-pattern.txt
  • 마지막으로 수정됨: 2021/11/22 13:55
  • 저자 127.0.0.1