본문 바로가기
프로그래밍/디자인패턴

데코레이터(Decorator) 패턴

by 리뷰하는 (게임)프로그래머_리프TV 2010. 4. 4.



특정 객체의 기능을 동적으로 추가하거나, 삭제 할 때 편리하게 사용 할 수 있는 패턴이다.

유용한 상황 :
 다른 객체에 영향을 주지 않으면서 새로운 기능을 추가 하고자 할 때, 특시 Client 측에서는 이렇게 새로운 기능이 추가된 객체와 그렇지 않은 객체를 따로 구분하고 싶지 않을 때
 
 특정 객체에 동적으로 추가된 기능을 삭제하고 싶을 때. 단, 이 경우 추가된 기능에 해당하는 객체가 아닌 원래 객체가 가진 기능은 삭제할 수 없다.
 
 클래스 상속을 통한 기능 확장이 불가능하거나 어려울 때, 예를 들어 서로 독립된 상속 관계가 많아 이들을 조합하면, 너무 많은 하위 클래스가 만들어질 우려가 있는 경우 또는 클래스 정의가 숨겨져 있어 상속이 불가능할 때.

예제는 기본 베이스 커피에 토핑을 추가 한다고 하였을 때 사용하는 데코레이터 패턴의 예제 이다.

소스에 대한 설명은 생략 하겠다.

#include <iostream>
#include <string>

class Beverage
{
public:
	// 순수 가상함수
	virtual double Cost() = 0;
	virtual std::string GetDescription() = 0;
	virtual ~Beverage(){}
};

class Espresso : public Beverage
{
public:
	double Cost()
	{
		return 1.99;
	}
	std::string GetDescription()
	{
		return "에스프레소";
	}
};

class HouseBlend : public Beverage
{
public:
	double Cost()
	{
		return 0.89;
	}
	std::string GetDescription()
	{
		return "하우스 브랜드 커피";
	}
};

class CondimentDecorator : public Beverage
{
	Beverage* pComponent_;
public:
	CondimentDecorator(Beverage* pObj) 
	{
		pComponent_ = pObj;
	}

	double Cost()
	{
		// 무언가를 가르키고 있다면 리스트 처럼 상위 동적 인스턴스를 따라가서,
		// return(cost)을 하게 된다.
		// 그렇게 하여 각 값들이 합쳐 지게 되는 것
		if( pComponent_ != 0 )
			return pComponent_->Cost();
		return 0;
	}
	std::string GetDescription()
	{
		// cost와 마찬가지
		if( pComponent_ != 0 )
			return pComponent_->GetDescription();
	}
	~CondimentDecorator()
	{
		// 소멸자에서 하나씩 찾아 삭제가 이루어진다.
		if( pComponent_ != NULL )
		{
			delete pComponent_;
		}
	}
};

class Milk : public CondimentDecorator
{
public:
	// 객체를 생성하면, 새로 할당하면서, 이전의 주소를 보관하게 된다.
	Milk( Beverage* pObj ) : CondimentDecorator( pObj ){}

	double Cost()
	{
		return 0.20 + CondimentDecorator::Cost();
	}
	std::string GetDescription()
	{
		return CondimentDecorator::GetDescription() + "+우유";
	}
};

class Mocha : public CondimentDecorator
{
public:
	Mocha(Beverage* pObj) : CondimentDecorator( pObj ) {}

	double Cost()
	{
		return 0.10 + CondimentDecorator::Cost();
	}
	std::string GetDescription()
	{
		return CondimentDecorator::GetDescription() + "+모카";
	}
};

void main()
{
	Beverage* houseblend = new HouseBlend;
	std::cout << houseblend->Cost() << std::endl;
	std::cout << houseblend->GetDescription() << std::endl;

	// 모카 추가
	houseblend = new Mocha(houseblend);
	std::cout << houseblend->Cost() << std::endl;
	std::cout << houseblend->GetDescription() << std::endl;

	// 모카를 1개 더 추가
	houseblend = new Mocha(houseblend);
	std::cout << houseblend->Cost() << std::endl;
	std::cout << houseblend->GetDescription() << std::endl;

	// 우유 추가
	houseblend = new Milk(houseblend);
	std::cout << houseblend->Cost() << std::endl;
	std::cout << houseblend->GetDescription() << std::endl;

	delete houseblend;
}
​


실행 결과