Pattern - Decorator

2 minute read

실패에 대해 걱정하지 마라. 한번만 제대로 하면 된다.

  • 드류 휴스턴 ( 드롭박스 공동 창업자 )

의도

객체에 동적으로 새로운 책임을 추가할 수 있게 합니다. 기능을 추가하기 위해서 서브클래스를 생성하는 것보다 더욱 융통성있는 방법을 제공합니다.


동기

  • 개별적인 객체에 새로운 책임을 추가할 필요가 있을 경우를 이야기 하는데, GUI 툴킷에서 모든 사용자 인터페이스 요소에는 필요 없지만, 어떤 사용자 인터페이스 요소에만 스크롤링과 같은 행동이나 테두리와 같은 속성을 추가할 수 있도록 해줄 필요가 있습니다.

  • 이와 같은 상황에서 가장 쉬운 방법은 이미 존재하는 클래스를 상속 받고, 또 다른 클래스에서 기존에 사용하던 동일한 클래스를 상속 받아 테두리 속성을 추가하고 이 서브 클래스의 인스턴스에 테두리가 있도록 하는 방법이지만, 이는 테두리을 선택하는 것 자체가 정적입니다. 사용자는 구성요소에 대해서 언제 , 어떻게 테두리로 장식해야할 지 제어할 수 없습니다. 이와 같이 기존의 객체에 대한 기능의 확장이 일어날 때 해당 구성요소를 둘러싸는 것입니다. 이렇게 무엇인가를 감싸는 객체를 장식자라고 합니다.

  • Decorater 의 옳지 않은 설계

  • Decorater 의 올바른 설계


활용성

  • 동적으로 또한 투명하게, 다시 말해 다른 객체에 영향을 주지 않고 개개의 객체에 새로운 책임을 추가하기 위해서 사용합니다.

  • 제거될 수 있는 책임에 대해서 사용됩니다.

  • 실제 상속으로 서브클래스를 계속 만드는 방법이 실질적이지 못할 때 사용합니다. 너무 많은 수의 독립된 확장이 가능할 때 모든 조합을 지원하기 위해 이를 상속으로 해결하면 클래스 수가 폭발적으로 많아지게 됩니다. 아니면, 클래스 정의가 숨겨지든가, 그렇지 않더라도 서브클래싱을 할 수 없게 됩니다.

  • 장식자 패턴은 객체에 새로운 행동을 추가할 수 있는 가장 효과적인 방법입니다. 장식자를 사용하면 장식자를 객체와 연결하거나 분리하는 작업을 통해 새로운 책임을 추가하거나 삭제하는 일이 런타임에 가능해집니다.


항목에 대한 설명

  • Component
    Visual Component
    동적으로 추가할 서비스를 가질 가능성이 있는 객체들에 대한 인터페이스

  • ConcreteComponent
    TextView
    추가적인 서비스가 실제로 정의되어야 할 필요가 있는 객체

  • Decorator
    Component
    Component 객체에 대한 참조자를 관리하면서 Component에 정의된 인터페이스를 만족하도록 인터페이스를 정의

  • ConcreteDecorator BorderDecorator, ScrollDecorator Component에 새롭게 추가할 서비스를 실제로 구현하는 서비스


코드 예제


package DesignPattern.gof_decorator;

public class Barrack {

    public static void main(String[] args) {
        Upgrade marinUpgrade = new MarineBasicUprade();

        marinUpgrade.upgrade();

        System.out.println("------------");

        Upgrade steampackUpgrade = new SteamPack(marinUpgrade);

        steampackUpgrade.upgrade();

        System.out.println("------------");

        Upgrade longRangeUpgrade = new LongRange(steampackUpgrade);

        longRangeUpgrade.upgrade();

        System.out.println("------------");

        Upgrade speedUpgrade = new Speed(longRangeUpgrade);

        speedUpgrade.upgrade();

        System.out.println("------------");

    }
}


package DesignPattern.gof_decorator;

public abstract class Upgrade {
    public abstract void upgrade();
}
                                    
package DesignPattern.gof_decorator;

public class MarineBasicUprade extends Upgrade {

    @Override
    public void upgrade() {
        System.out.println("공격력이 +1 증가하였습니다.");
    }
}

package DesignPattern.gof_decorator;

public abstract class UpgradeDecorator extends Upgrade {

    protected Upgrade upgrade;

    public UpgradeDecorator(Upgrade upgrade){
        this.upgrade = upgrade;
    }

    public void upgrade(){
        upgrade.upgrade();
    }
}

package DesignPattern.gof_decorator;

public class SteamPack extends UpgradeDecorator {
    public SteamPack(Upgrade upgrade) {
        super(upgrade);
    }

    @Override
    public void upgrade() {
        super.upgrade();

        applySteamPack();
    }

    public void applySteamPack(){
        System.out.println("스팀팩이 적용되었습니다. ");
    }
}

package DesignPattern.gof_decorator;

public class Speed extends UpgradeDecorator {

    public Speed(Upgrade upgrade){
        super(upgrade);
    }

    public void upgrade() {
        super.upgrade();

        applySteamUpgrade();
    }

    public void applySteamUpgrade(){
        System.out.println("속도가 10 만큼 증가하였습니다.");
    }
}

package DesignPattern.gof_decorator;

public class LongRange extends UpgradeDecorator {
    public LongRange(Upgrade upgrade) {
        super(upgrade);
    }

    @Override
    public void upgrade() {
        super.upgrade();

        applyLongRange();
    }

    public void applyLongRange(){
        System.out.println("공격 사거리가 10 증가하였습니다.");
    }
}