객체의 결합을 통해 기능을 동적으로 유연하게 확장할 수 있게 해주는 패턴
즉, 기본 기능에 추가할 수 있는 기능의 종류가 많은 경우에 각 추가 기능을 Decorator 클래스로 정의한 후, Decorator 객체를 조합함으로써 추가 기능의 조합을 설계하는 방식

기본 기능에 추가할 수 있는 많은 종류의 부가 기능에서 파생되는 다양한 조합을 동적으로 구현할 수 있게 하는 패턴이다.
| Component : 기본 기능을 뜻하는 ConcreteComonent와 추가 기능을 뜻하는 Decorator의 공통 기능을 정의 |
| ConcreteComponent : 기본 기능을 구현하는 클래스 |
| Decorator : 많은 수가 존재하는 구체적인 Decorator의 공통 기능을 제공 |
| ConcreteDecoratorA, ConcreteDecoratorB : Decorator의 하위 클래스로 기본 기능에 추가되는 개별적인 기능을 뜻함. ConcreteDecorator 클래스는 ConcreteComponent 객체에 대한 참조가 필요한데, 이는 Decorator 클래스에서 Component클래스로의 '합성(composition)의 관계'를 통해 표현 |
| 구조 패턴 : - 클래스나 객체를 조합해 더 큰 구조를 만드는 패턴 - 예를 들어, 서로 다른 인터페이스를 지닌 2개의 객체를 묶어 단일 인터페이스를 제공하거나 객체들을 서로 묶어 새로운 기능을 제공하는 패턴이다. |
| 합성 관계 : - 생성자에서 필드에 대한 객체를 생성하는 경우 - 전체 객체의 라이프 타임과 부분 객체의 라이프 타임은 의존적이다. - 즉, 전체 객체가 없어지면 부분 객체도 없어진다. |
| 데코레이터 패턴 사용하는 경우 - 클래스의 요소들을 계속해서 수정하며 사용하는 구조가 필요한 경우 - 여러 요소들을 조합해서 사용하는 클래스 구조인 경우 |
예를 들어 커피를 주문하는 코드를 작성한다.
abstract Beverage
public abstract class Beverage {
private String description;
private double cost;
public void setDescription(String description) {
this.description = description;
}
public String getDescription() {
return this.description;
}
public void setCost(double cost) {
this.cost = cost;
}
public double getCost() {
return this.cost;
}
}
public class Espresso extends Beverage {
public Espresso() {
setDescription("espresso");
setCost(1.00);
}
}
public class MilkEspresso extends Beverage{
public MilkEspresso(Beverage beverage) {
setDescription(beverage.getDescription().concat(", Milk"));
setCost(beverage.getCost()+0.20);
}
}
public class WhipEspresso extends Beverage{
public WhipEspresso(Beverage beverage) {
setDescription(beverage.getDescription().concat(", Whip"));
setCost(beverage.getCost()+0.10);
}
}
public class MilkWhipEspresso extends Beverage {
public MilkWhipEspresso(Beverage beverage) {
setDescription(beverage.getDescription().concat(", Milk"));
setDescription(getDescription().concat(", Whip"));
setCost(beverage.getCost()+0.20);
setCost(getCost()+0.10);
}
}
Main
public class Main {
public static void main(String[] args) {
Beverage espresso = new Espresso();
System.out.println("description : "+espresso.getDescription() + " / cost : "+espresso.getCost());
Beverage milkEspresso = new MilkEspresso(espresso);
System.out.println("description : "+milkEspresso.getDescription() + " / cost : "+milkEspresso.getCost());
Beverage whipEspresso = new WhipEspresso(espresso);
System.out.println("description : "+whipEspresso.getDescription() + " / cost : "+whipEspresso.getCost());
Beverage milkWhipEspresso = new MilkWhipEspresso(espresso);
System.out.println("description : "+milkWhipEspresso.getDescription() + " / cost : "+milkWhipEspresso.getCost());
}
}
이와 같이 상속을 통해 조합의 각 경우를 설계한다면 각 조합별로 하위 클래스는 계속 구현되어야 할 것이다.
즉, 다양한 기능의 조합을 고려해야 하는 경우 상속을 통한 기능의 확장은 각 기능별로 클래스를 추가해야 한다는 단점이 있다.
메뉴의 가지 수가 많아질수록 문제는 더욱더 심각해질 것이다.
| 경우 | 기본 | 추가 | 클래스 명 | |
| Milk | Whip | |||
| 1 | O | Espresso | ||
| 2 | O | O | MilkEspresso | |
| 3 | O | O | WhipEspresso | |
| 4 | O | O | O | MilkWhipEspresso |
| ... | ... | ... | ... | ... |
abstract Beverage
public abstract class Beverage {
private String description;
protected void setDescription(String description) {
this.description = description;
}
public String getDescription() {
return this.description;
}
protected abstract double getCost();
}
public class Espresso extends Beverage {
public Espresso() {
setDescription("espresso");
}
@Override
protected double getCost() {
return 1.99;
}
}
abstract BeverageDecorator
public abstract class BeverageDecorator extends Beverage{
private Beverage beverage;
private double cost = 0.0;
protected void setCost(double cost) {
this.cost = cost;
}
protected void setBeverage(Beverage beverage) {
this.beverage = beverage;
}
public Beverage getBeverage() {
return this.beverage;
}
protected void setDecorationCost(double cost) {
this.cost = cost;
}
protected double getDecorationCost() {
return this.cost;
}
}
public class Milk extends BeverageDecorator{
public Milk(Beverage beverage) {
setBeverage(beverage);
setDecorationCost(0.20);
}
@Override
public String getDescription() {
return getBeverage().getDescription().concat(", Milk");
}
@Override
protected double getCost() {
return getBeverage().getCost() + getDecorationCost();
}
}
public class Whip extends BeverageDecorator{
public Whip(Beverage beverage) {
setBeverage(beverage);
setDecorationCost(0.10);
}
@Override
public String getDescription() {
return getBeverage().getDescription().concat(", Whip");
}
@Override
protected double getCost() {
return getBeverage().getCost() + getDecorationCost();
}
}
Main
public class Main {
public static void main(String[] args) {
// 기본 에스프레소
Beverage beverage = new Espresso();
String description = beverage.getDescription();
double cost = beverage.getCost();
System.out.println(description + " / cost "+cost);
// 우유 추가
beverage = new Milk(beverage);
description = beverage.getDescription();
cost = beverage.getCost();
System.out.println(description + " / cost "+cost);
// 휘핑 추가
beverage = new Whip(beverage);
description = beverage.getDescription();
cost = beverage.getCost();
System.out.println(description + " / cost "+cost);
}
}
장점 :
- 기존 코드를 수정하지 않고 확장할 수 있다.(OCP 원칙 만족)
- 구성과 위임을 통해서 실행중에 새로운 행동을 추가할 수 있다.
단점 :
- 특정 구성요소인지 확인한 다음 어떤 작업을 처리하는 경우에 데코레이터 패턴을 사용하기 어려울 수 있다.
ex : 특정 커피는 할인이 안되는 경우.
- 클래스가 많이 추가되는 경우 복잡해질 수 있다.
참고 :
[디자인패턴] 3. 데코레이터 패턴 개념과 예제 (decorator pattern)
Head First Design Patterns 책을 보고 정리한 내용입니다. 디자인 패턴을 처음 입문하시는 분들께 추천드리고픈 책입니다. >[ 목차 ] 스트래티지 패턴 옵저버 패턴 데코레이터 패턴 팩토리 패턴 싱글턴
velog.io
https://gmlwjd9405.github.io/2018/07/09/decorator-pattern.html
[Design Pattern] 데코레이터 패턴이란 - Heee's Development Blog
Step by step goes a long way.
gmlwjd9405.github.io
https://coding-factory.tistory.com/713
[Design Pattern] 데코레이터 패턴(Decorator pattern)에 대하여
데코레이터(Decorator pattern) 패턴이란? 데코레이터 패턴(Decorator Pattenr)은 주어진 상황 및 용도에 따라 어떤 객체에 책임(기능)을 동적으로 추가하는 패턴을 말합니다. 데코레이터라는 말 그대로 장
coding-factory.tistory.com
| [Design Pattern] State Pattern (0) | 2023.03.26 |
|---|---|
| [Design Pattern] Builder Pattern (0) | 2023.03.25 |
| [Design Pattern] Strategy Pattern (0) | 2023.03.23 |
| [Design Pattern] Abstract Factory Pattern (0) | 2023.03.22 |
| [Design Pattern] Command Pattern (0) | 2023.03.21 |