Note

[디자인패턴] Decorator Pattern - 데코레이터 패턴

Supreme_YS 2021. 12. 18. 13:13

Decorator Pattern은 단어에서 주는 느낌 그대로 받아들이면 될 듯하다. 데코레이터는 말 그대로 원래의 기본 무언가에서 추가로 꾸며주는 역할을 하는 것을 칭한다. 데코레이터 패턴도 마찬가지다. 기본 기능이 있고, 추가 기능을 덧붙일 때 사용하게 되는 패턴이다. 이 패턴을 사용하게 되면 좋은 이유는 서브 클래스 수를 늘리지 않아도 된다. 이 말이 무슨 헛소리인가. 이상한 소리처럼 들릴 수 있겠지만 차근차근 예제를 통해 살펴보도록 하자.

 

개인적으로 서브웨이를 참 좋아한다. 그리고 야채를 많이 넣는 것을 굉장히 좋아한다. 또 어느 누군가는 육류를 많이 넣는 것을 좋아하고 야채를 싫어하는 사람도 있다. 또 야채와 고기의 비율을 중요시 여기는 사람도 있다. 그래서 야채파, 고기파, 야채 + 고기파 이렇게 세 분류의 사람이 있다고 가정해봅시다. 

 

먼저, 야채파의 결과물은 야채샌드위치 일 것이고 고기파는 고기샌드위치, 그리고 고기+야채 샌드위치 일 것이다. 공통적으로 빵이 있다는 것을 잊으면 안된다. 이 것을 직관적인 코드로 설계하면 공통인 메서드 만든다(make()) 를 상속받는 공통적인 빵(bread)이 있을 것이고, 추가적으로 VegSandwich, MeatSandwich, MixSandwich 이렇게 세 개의 클래스를 새로 생성할 것이다. 근데 위의 예시인 서브웨이는 커스터마이징의 끝이다. 그러면 매번 이런 서브 클래스들을 늘려갈 순 없는 노릇이잖습니까? 거기 토핑 종류가 몇 갠데.... 그걸 수학적으로 순열과 조합을 적용해보면 굉장히 많은 양의 서브 클래스가 생길듯.. 이러한 상황을 막기 위해서 데코레이터 패턴을 사용하는 것이다.

 

이런 식으로 토핑 데코레이터를 만들고, Meat Decorator, Veg Decorator를 만듦으로써 서브 클래스 생성이 필요가 없고, 오히려 새로운 토핑이 나오면 단순히 토핑 데코레이터를 상속받는 새로운 클래스만 하나 추가해주면 되니까 굉장히 관리가 용이해진다. 실제 코드 예시를 통해 이해를 보충해봅시다.

 

* 샌드위치를 만드는 공통된 추상 메서드!

package DesignPattern.Decorator;

public abstract class Sandwich {
    public abstract void make();
}

* 공통적으로 빵은 필수져?

package DesignPattern.Decorator;

public class Bread extends Sandwich{
    public void make() {
        System.out.println("Add Bread");
    }
}

* 토핑 데코레이터 - 모든 토핑들을 관리하는 토핑 관리자

package DesignPattern.Decorator;

public class ToppingDecorator extends Sandwich{
    private Sandwich sandwich;

    public ToppingDecorator(Sandwich sandwich) {
        this.sandwich = sandwich;
    }

    public void make() {
        sandwich.make();
    }
}

* 토핑들

package DesignPattern.Decorator;

public class CheeseDecorator extends ToppingDecorator {

    public CheeseDecorator(Sandwich sandwich) {
        super(sandwich);
    }

    public void make() {
        super.make();
        addCheese();
    }

    private void addCheese() {
        System.out.println("Add Cheese");
    }
}
package DesignPattern.Decorator;

public class LettuceDecorator extends ToppingDecorator{

    public LettuceDecorator(Sandwich sandwich) {
        super(sandwich);
    }

    public void make() {
        super.make();
        addLettuce();
    }

    private void addLettuce() {
        System.out.println("Add Lettuce");
    }

}

* 테스트 코드 

package DesignPattern.Decorator;

public class Client {
    public static void main(String[] args) {
        // Lettuce Sandwich
        Sandwich sandwichWithLettuce = new LettuceDecorator(new Bread());
        sandwichWithLettuce.make();
        System.out.println("------------------");

        // Lettuce + Cheese Sandwich
        Sandwich sandwichWithLettuceAndCheese = new CheeseDecorator(new LettuceDecorator(new Bread()));
        sandwichWithLettuceAndCheese.make();
    }
}
Add Bread
Add Lettuce
------------------
Add Bread
Add Lettuce
Add Cheese