Skip to content

Decorator Pattern

Glory Day edited this page Jun 15, 2022 · 11 revisions

About

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

UML Class

UML_Class_1

데코레이터 패턴 UML 클래스


  • Component : 기본 기능을 뜻하는 ConcreteComponent와 추가 기능을 뜻하는 Decorator의 공통 기능을 정의. 즉, 클라이언트는 Component를 통해 실제 객체를 사용한다.
  • ConcreteComponent : 기본 기능을 구현하는 클래스.
  • Decorator : 많은 수가 존재하는 구체적인 Decorator의 공통 기능을 제공.
  • ConcreteDecoratorA, ConcreteDecoratorB
    • Decorator의 하위 클래스로 기본 기능에 추가되는 개별적인 기능을 뜻한다.
    • ConcreteDecorator 클래스는 ConcreteComponent 객체에 대한 참조가 필요한데, 이는 Decorator 클래스에서 Component 클래스로의 합성(Composition) 관계를 통해 표현된다.

Reference

합성 관계

생성자에서 필드에 대한 객체를 생성하는 경우이다. 전체 객체의 라이프 타임과 부분 객체의 라이프 타임은 의존적이다. 즉, 전체 객체가 없어지면 부분 객체도 없어진다.

Example

UML_Class_2

커피 음료 UML 클래스


Beverage 클래스는 음료를 나타내는 추상 클래스이며, 커피 클래스들은 Beverage 클래스를 상속 받아서 cost()를 각각 가격에 맞추어 구현하는 구조이다.

UML_Class_3

새롭게 커피 음료들을 추가하는 경우의 UML 클래스


고객은 커피를 주문할 때 우유나 두유, 모카나 휘핑크림을 추가하기도 한다. 따라서 위와 같은 구조로 커피 음료들을 구성한다.

problem

커피의 종류가 별로 없는 경우에는 상관없지만, 커피의 종류가 계속 추가되는 경우에는 위 UML처럼 새로운 커피 클래스를 계속 작성해서 추가해줘야한다.

UML_Class_4

첨가물들을 추가하는 경우의 UML 클래스


위의 경우 너무 많은 클래스를 생성하게 되는 문제점을 가지고 있다. 따라서 첨가물의 유무에 따라 커피의 종류와 가격을 정하도록 Beverage 클래스에 재료와 관련된 메소드를 구현하고 상속받는 클래스마다 첨가물의 유무를 반환하는 형식으로 구조를 만들었다.

위와 같은 구조는 다음과 같은 문제에 직면하게 된다.

  • 첨가물 가격이 바뀔 때마다 기존 코드를 수정해야 한다.
  • 첨가물의 종류가 많아지면 새로운 메소드를 추가해야 하고, 슈퍼클래스의 cost() 메소드도 수정해야 한다.
  • 만약 새로운 음료에, 첨가물이 들어가면 안되는 경우에 첨가물 관련 메소드를 그대로 상속 받는다.
  • 첨가물을 두 번 이상 첨가하는 음료의 경우에 문제가 발생한다.

Solution

UML_Class_5

데코레이터 패턴을 적용한 UML 클래스


커피 클래스들과 CondimentDecorator 클래스는 Beverage 클래스를 상속받고, 또한 첨가물 클래스들은 CondimentDecorator 클래스를 상속받는다.
CondimentDecorator 클래스의 필드에는 Beverage 타입의 변수가 들어있는데, 이 변수를 이용해 아래 첨가물들의 메소드 getDescription(), cost()는 다음과 같이 작성된다.

string getDescription() {
    return beverage->getDescription() + " + 첨가물 이름";
}

double cost() {
    return beverage->cost() + 첨가물 가격;
}

커피 객체를 먼저 생성한 다음, 첨가물 객체의 생성자에 생성한 커피 객체를 넣어주면 해당 첨가물 객체에서 getDescription(), cost() 메소드를 불러올 때, 추가된 값으로 반환된다.

Code

Clone this wiki locally