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介面,增加新的公開的行為,實現“半透明”的裝飾者模式。在實際專案中要做出最佳選擇。