데코레이터 패턴
- 주어진 상황 및 용도에 따라 어떤 객체에 책임을 덧붙이는 패턴
- 객체에 추가적인 요건을 동적으로 첨가하며, 기능 확장이 필요할 때 서브 클래싱 대신 쓸 수 있는 유연한 대안이 될 수 있다.
Component
- 실질적인 인스턴스를 컨트롤하는 역할
ConcreteComponent
- Component의 실질적인 인스턴스의 부분이며 책임의 주체의 역할
Decorator
- Component와 ConcreteDecorator를 동일시하도록 해주는 역할
ConcreteDecorator
- 실질적인 장식 인스턴스 및 정의이며 추가된 책임의 주체 부분
예제
커피 주문을 예시로 들어서 진행해 보자.
Component
public interface Beverage {
public double cost();
public String getDescription();
}
- Beverage 인터페이스가 Component이다.
- 즉 여기서 실질적인 인스턴스를 컨트롤하게 된다.
ConcreteComponent
public class Espresso implements Beverage {
@Override
public double cost() {
return 2000;
}
@Override
public String getDescription() {
return "에스프레소";
}
}
public class HouseBlend implements Beverage {
@Override
public double cost() {
return 3000;
}
@Override
public String getDescription() {
return "하우스 블렌드 커피";
}
}
- 음료들이 Beverage를 구현하므로 ConcreteComponent가 된다.
- 이 음료들이 책임의 주체가 된다.
Decorator
public interface Decorator extends Beverage {
public String getDescription();
public double cost();
}
- 어떤 책임을 덧붙이기 위해 사용되는 Decorator로 여기서는 음료의 옵션을 덧붙이기 위해 사용된다.
- Component를 상속받아 만들어 Component와 ConcreteDecorator를 이어주는 역할을 한다.
ConcreteDecorator
public class Whip implements Decorator {
private Beverage beverage;
public Whip(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ", 휘핑 크림";
}
@Override
public double cost() {
return beverage.cost() + 1500;
}
}
public class MoCha implements Decorator {
private Beverage beverage;
public MoCha(Beverage beverage) {
this.beverage = beverage;
}
@Override
public double cost() {
return beverage.cost() + 1000;
}
@Override
public String getDescription() {
return beverage.getDescription() + ", 모카";
}
}
- 커피의 옵션을 구현한 ConcreteDecorator이다.
- 최상위 부모인 Beverage를 멤버 변수로 가지고 있다.
- 여기서는 getDescription과 cost()를 수행할 때 자신의 옵션과 함께 가지고 있는 beverage의 메서드도 함께 실행한다.
결과
public class Test {
public static void main(String[] args) {
Beverage beverage1 = new HouseBlend();
beverage1 = new MoCha(beverage1);
beverage1 = new Whip(beverage1);
System.out.println("가격: " + beverage1.cost());
System.out.println("메뉴: " + beverage1.getDescription());
}
}
가격: 5500.0
메뉴: 하우스 블렌드 커피, 모카, 휘핑 크림
- beverage1은 처음에 ConcreteComponent 중 하나인 HouseBlend 인스턴스를 생성한다.
- 그 후에 옵션 중 하나인 ConcreteDecorator의 MoCha를 생성하는데 생성자 아규먼트로 beverage1을 넣어준다.
- 즉 Mocha가 HouseBlend를 감싼 것이다.
- 그 후 또 Whip을 생성하여 한번 더 감싼다.
- 즉 Whip -> Mocha -> HouseBlend순으로 감싸게 되었다.
- 그 후 출력을 위해 메서드를 호출하게 되면 Whip의 method가 호출될 것이고 그때 Whip이 가지고 있는 (Mocha -> HouseBlend)의 메서드가 호출되고 마지막으로 HouseBlend의 메서드가 호출된다.
- 그렇게 호출 스택이 쌓이고 빠져나가면서 하나씩 실행되므로 위와 같은 출력 결과가 나온 것을 확인할 수 있다.
데코레이터 패턴을 이용하면 음료들의 옵션을 음료 각각의 구현을 변경하지 않고 유연하게 옵션을 추가할 수 있다.
'디자인 패턴' 카테고리의 다른 글
싱글톤 패턴(Singleton Pattern) (0) | 2019.12.12 |
---|---|
추상 팩토리 패턴(Abstract Factory Pattern) (0) | 2019.12.12 |
팩토리 메서드 패턴(Factory Method Pattern) (0) | 2019.12.11 |
옵저버 패턴(Observer Pattern) (0) | 2019.12.10 |
스트래티지 패턴(Strategy Pattern) (0) | 2019.12.10 |