데코레이터 패턴(Decorator Pattern)

  • 객체에 새로운 기능을 추가하려면 클래스를 확장해야 합니다.
  • 클래스를 확장하는 방법은 상속구성(복합 객체 구성) 두 가지입니다.
  • 상속은 클래스 간 강력한 결합 관계를 만들어 객체의 확장성과 유연성을 방해합니다.
  • 데코레이터 패턴은 런타임 상황에서 객체를 동적으로 확장합니다.

데코레이터 패턴 구조

  • 데코레이터 패턴은 런타임 상황에서 객체에 새로운 책임을 동적으로 추가합니다.
  • 런타임 상황에서 책임을 추가한다는 것은 책임이 향후 어떤 형태로 변경될지 모른다는 것입니다.
  • 따라서 데코레이터 패턴은 책임(클래스)를 분산하여 설계하는 것이 핵심입니다.
  • 데코레이터 패턴은 다음과 같은 구성 요소가 있습니다.
    • 컴포넌트(Component)
      • 최상위 인터페이스 클래스입니다.
    • 컴포넌트 구현(concrete Component)
      • 인터페이스의 실제 구현을 작성합니다.
    • 데코레이터(Decorator)
      • Component를 참조하여 인터페이스를 일치화합니다.
    • 데코레이터 구현(concrete Decorator)
      • Decorator의 실제 구현(객체의 확장 및 책임의 추가)를 작성합니다.

데코레이터 패턴 예제

  • 인터넷 쇼핑몰에서 옷과 컴퓨터 부품을 구매하는 시나리오를 작성하겠습니다.
  • 데코레이터 패턴은 물건을 구매할 때 옵션을 추가하는 것과 같은 원리를 가집니다.
  • 데코레이터 패턴은 또한 작은 클래스가 많이 만들어집니다. 따라서 클래스 이름을 데코레이터 패턴의 구성 요소로 작성하겠습니다.

데코레이터 패턴: 컴포넌트(Component)

  • Component 클래스는 최상위 인터페이스 클래스입니다.
  • 공통된 기능을 가상 함수로 작성합니다.
1
2
3
4
5
6
7
// Component
class Component
{
public:
	virtual std::string product() = 0;
	virtual int price() = 0;
};

데코레이터 패턴: 컴포넌트 구현(Concrete Component)

  • Component 클래스의 실제 구현을 작성합니다.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// ConcreteComponent
class ConcreteComponent1 :
	public Component
{
public:
	std::string product()
	{
		return "T-Shirts";
	}
	int price()
	{
		return 20000;
	}
};

// ConcreteComponent
class ConcreteComponent2 :
	public Component
{
public:
	std::string product()
	{
		return "Y-Shirts";
	}
	int price()
	{
		return 30000;
	}
};

데코레이터 패턴: 데코레이터(Decorator)

  • 데코레이터 패턴을 적용하기 위해 Component 클래스를 상속합니다.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Decorator
class Decorator : 
	public Component
{
public:
	Decorator(std::shared_ptr<Component> component) :
		m_component(component)
	{

	}

	std::string product()
	{
		return m_component->product();
	}

	int price()
	{
		return m_component->price();
	}

private:
	std::shared_ptr<Component> m_component;
};

데코레이터 패턴: 데코레이터 구현(concrete Decorator)

  • 데코레이터 패턴의 실제 구현을 작성합니다.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// concretedecorate
class concretedecorator1 :
	public Decorator
{
public:
	concretedecorator1(std::shared_ptr<Component> component) :
		Decorator(component)
	{

	}

	std::string product()
	{
		return Decorator::product() + ", " + "i7";
	}

	int price()
	{
		return Decorator::price() + 46000;
	}
};

// concretedecorate
class concretedecorator2 :
	public Decorator
{
public:
	concretedecorator2(std::shared_ptr<Component> component) :
		Decorator(component)
	{

	}

	std::string product()
	{
		return Decorator::product() + ", " + "ssd256";
	}

	int price()
	{
		return Decorator::price() + 100000;
	}
};

데코레이터 패턴: 실행 코드

  • 데코레이터 패턴은 객체의 생성 시 연속적인 형태로 작성합니다.
  • 따라서 코드가 매우 넓어집니다.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
int main(const int argc, const char* argv[])
{
	
	std::shared_ptr<Component> p = std::make_shared<concretedecorator2>(
		std::make_shared<concretedecorator1>(
		std::make_shared<Decorator>(
		std::make_shared<ConcreteComponent1>()))
		);

	std::cout << p->price() << std::endl;

	return 0;
}
  • 실행 결과
1
2
T-Shirts, i7, ssd256
166000