1. 程式人生 > >08:裝飾者模式

08:裝飾者模式

一、介紹

 

    裝飾器模式(Decorator Pattern允許向一個現有的物件新增新的功能,同時又不改變其結構。這種型別的設計模式屬於結構型模式,它是作為現有的類的一個包裝。這種模式建立了一個裝飾類,用來包裝原有的類,並在保持類方法簽名完整性的前提下,提供了額外的功能。

 

二、應用場景

1. 需要擴充套件一個類的功能,或給一個類新增附加職責。

2. 需要動態的給一個物件新增功能,這些功能可以再動態的撤銷。

3. 需要增加由一些基本功能的排列組合而產生的非常大量的功能,從而使繼承關係變的不現實。

4. 當不能採用生成子類的方法進行擴充時。一種情況是,可能有大量獨立的擴充套件,為支援每一種組合將產生大量的子類,使得子類數目呈爆炸性增長。另一種情況可能是因為類定義被隱藏,或類定義不能用於生成子類。

 

三、要點

1. 多組合,少繼承。

利用繼承設計子類的行為,是在編譯時靜態決定的,而且所有的子類都會繼承到相同的行為。然而,如果能夠利用組合的做法擴充套件物件的行為,就可以在執行時動態地進行擴充套件。

2. 類應設計的對擴充套件開放,對修改關閉。

 

 

四、樣例

在星巴克購買咖啡時,可以要求在其中加入各種調味品(輔料)。調味品很多,有些不收費(例如:白砂糖、香草粉等),有些則需要額外收費(例如:奶油、摩卡、糖漿等),所以充分利用起來吧!倘若咖啡不帶勁,我們想要新增奶油、摩卡和糖漿,這時,就可以利用裝飾者模式思想來實現。

 

1、建立構建

先定義所有飲料基類,並提供名稱和價錢

// component.h
#ifndef COMPONENT_H
#define COMPONENT_H

#include <string>
using namespace std;

class IBeverage
{
public:
	virtual string Name() = 0;
	virtual double Cost() = 0;
};

#endif

2、建立具體構建

// concrete_component.h
#ifndef CONCRETE_COMPONENT_H
#define CONCRETE_COMPONENT_H

#include "component.h"

/******** 具體飲料(咖啡) ********/
// 黑咖啡
class HouseBlend :public Ibeverage
{
public:
	string Name()
	{
		return "HouseBlend";
	}

	double Cost()
	{
		return 30.0;
	}
};

// 深度烘焙咖啡
class DarkRoast :public IBeverage
{
public:
	string Name()
	{
		return "DarkRoast";
	}

	double Cost()
	{
		return 28.5
	}
};
#endif

3、建立裝飾

咖啡有了,剩下的就是新增調味品,其同樣繼承IBeverage,並持有咖啡的例項

// decorator.h
#ifndef DECORATOR_H
#define DECORATOR_H

#include "component.h"

// 調味品
class CondimentDecorator :public IBeverage
{
public:
	CondimentDecorator(IBeverage *beverage) :m_pBeverage(beverage) {}

	string Name()
	{
		return m_pBeverage->Name();
	}

	double Cost()
	{
		return m_pBeverage->Cost();
	}

protected:
	IBeverage *m_pBeverage;

};
#endif

4、建立具體裝飾

// concrete_decorator.h
#ifndef CONCRETE_DECORATOR_H
#define CONCRETE_DECORATOR_H

#include "decorator.h"

/***** 具體的飲料(調味品) ******/

// 奶油
class Cream : public CondimentDecorator
{
public:
	Cream(IBeverage *beverage) : CondimentDecorator(beverage) {}

	string Name()
	{
		return m_pBeverage->Name() + " Cream";
	}

	double Cost()
	{
		return m_pBeverage->Cost() + 3.5;
	}
};

// 摩卡
class Mocha : public CondimentDecorator
{
public:
	Mocha(IBeverage *beverage) : CondimentDecorator(beverage) {}

	string Name()
	{
		return m_pBeverage->Name() + " Mocha";
	}

	double Cost()
	{
		return m_pBeverage->Cost() + 2.0;
	}
};

// 糖漿
class Syrup :public CondimentDecorator
{
public:
	Syrup(IBeverage *beverage) :CondimentDecorator(beverage) {}

	string Name()
	{
		return m_pBeverage->Name() + " Syrup";
	}

	double Cost()
	{
		return m_pBeverage->Cost() + 3.0;
	}
};

#endif

5、建立客戶端

// main.cpp
#include "concrete_component.h"
#include "concrete_decorator.h"
#include <iostream>

#ifndef SAFE_DELETE
#define SAFE_DELETE(p) { if(p){ delete(p); (p) = NULL; } }
#endif

int main()
{
	/***** 黑咖啡 *******/
	IBeverage *pHouseBlend = new HouseBlend();
	cout << pHouseBlend->Name() << ":" << pHouseBlend->Cost() << endl;

	// 黑咖啡+奶油
	CondimentDecorator *pCream = new Cream(pHouseBlend);
	cout << pCream->Name() << ":" << pCream->Cost() << endl;

	// 黑咖啡+摩卡
	CondimentDecorator *pMocha = new Mocha(pHouseBlend);
	cout << pMocha->Name() << ":" << pMocha->Cost() << endl;

	// 黑咖啡+糖漿
	CondimentDecorator *pSyrup = new Syrup(pHouseBlend);
	cout << pSyrup->Name() << ":" << pSyrup->Cost() << endl;

	/******* 深度烘焙咖啡 ********/
	IBeverage *pDarkRoast = new DarkRoast();
	cout << pDarkRoast->Name() << ":" << pDarkRoast->Cost() << endl;

	// 深度烘焙+奶油
	CondimentDecorator *pCreamDR = new Cream(pDarkRoast);
	cout << pCreamDR->Name() << ":" << pCreamDR->Cost() << endl;

	// 深度烘焙+奶油+摩卡
	CondimentDecorator *pCreamMocha = new Mocha(pCreamDR);
	cout << pCreamMocha->Name() << ":" << pCreamMocha->Cost() << endl;

	// 深度烘焙+奶油+摩卡+糖漿
	CondimentDecorator *pCreamMochaSyrup = new Syrup(pCreamMocha);
	cout << pCreamMochaSyrup->Name() << ":" << pCreamMochaSyrup->Cost() << endl;

	SAFE_DELETE(pSyrup);
	SAFE_DELETE(pMocha);
	SAFE_DELETE(pCream);
	SAFE_DELETE(pHouseBlend);

	SAFE_DELETE(pCreamMochaSyrup);
	SAFE_DELETE(pCreamMocha);
	SAFE_DELETE(pCreamDR);
	SAFE_DELETE(pDarkRoast);

	getchar();
	return 0;
}

6、輸出

HouseBlend Cream : 33.5
HouseBlend Mocha : 32
HouseBlend Syrup : 33
DarkRoast : 28.5
DarkRoast Cream : 32
DarkRoast Cream Mocha : 34
HouseBlend : 30
DarkRoast Cream Mocha Syrup : 37

五、優缺點

優點

1. Decorator模式與繼承關係的目的都是要擴充套件物件的功能,但是Decorator可以提供比繼承更多的靈活性。

2. 通過使用不同的具體裝飾類以及這些裝飾類的排列組合,設計師可以創造出很多不同行為的組合。

 

缺點

1. 這種比繼承更加靈活機動的特性,也同時意味著更加多的複雜性。

2. 裝飾模式會導致設計中出現許多小類,如果過度使用,會使程式變得很複雜。

3. 裝飾模式是針對抽象元件(Component)型別程式設計。但是,如果你要針對具體元件程式設計時,就應該重新思考你的應用架構,以及裝飾者是否合適。當然也可以改變Component介面,增加新的公開的行為,實現“半透明”的裝飾者模式。在實際專案中要做出最佳選擇。