By Trashtoy - My own work, written with text editor., Public Domain, Link
Decorator pattern 的類圖表明了所有處理、使用的對象都是 Component 介面,能夠操作的只有 Component 開放的功能,也就是 operation(),任何 Component 的實作類別都必須依自己的情況實作 operation()。其中有一個 Component 的延伸介面 Decorator,對 operation() 有一個明確的定義--除了做自己的事之外,還要呼叫的包裹對象的 operation()。
為什麼叫 Decorator pattern,因所設定的目標是在執行時期為所處理的物件附加行為,且不會影所處理物件,有人拿來當作是類別繼承的替代模式,但兩者之間還是有根本性的不同,類別繼承可以增加功能(增加成員函數),但 Decorator pattern 只能增加行為,也就是為 Component::operation() 增加操作的內容。
在 C++11 以前(也就是 C++98)做起來都不夠完美,因對虛擬函數少了 final 關鍵字的支援,先看看以下的範例(範例取材自 Head First Design Patterns In C Sharp)
#include <memory>
#include <iostream>
using namespace std;
// Component (interface)
// 飲料介面
class IBeverage
{
public:
IBeverage(){}
virtual ~IBeverage(){}
// 回報費用
virtual float Cost() const = 0;
};
// ConcreteComponent
// 飲料的實作-咖啡
class CCoffee:public IBeverage
{
// 回報費用
virtual float Cost() const override
{
return 10;
}
public:
CCoffee(){}
~CCoffee(){}
};
// Decorator (interface)
// 裝飾者介面
class IDecorator :public IBeverage
{
// 包裹被裝飾的物件
shared_ptr<IBeverage> IBeverage_Ptr;
// 子類別必須提供增加的費用
virtual float AddCost() const = 0;
// 回報費用
// 重新定義行為且不希望被修改
virtual float Cost() const override final
{
return IBeverage_Ptr->Cost() + AddCost();
}
public:
IDecorator(const shared_ptr<IBeverage> &IBeverage_Arg)
:IBeverage_Ptr(IBeverage_Arg)
{}
~IDecorator(){}
};
class Mocha :public IDecorator
{
// 子類別必須提供增加的費用
virtual float AddCost() const override
{
return 1.3;
}
public:
Mocha(const shared_ptr<IBeverage> &IBeverage_Arg)
:IDecorator(IBeverage_Arg)
{}
~Mocha() {}
};
int main()
{
// 產生咖啡物件
shared_ptr<IBeverage> Coffee_Ptr(new CCoffee);
// 為咖啡附加 Mocha
shared_ptr<IBeverage> Mocha1_Ptr(new Mocha(Coffee_Ptr));
// 雙倍 Mocha 咖啡
shared_ptr<IBeverage> Mocha2_Ptr(new Mocha(Mocha1_Ptr));
cout << "雙倍 Mocha 咖啡的費用是:" << Mocha2_Ptr->Cost() << endl;
return 0;
}
看看 IDecorator::Cost() 的定義
// 回報費用
// 重新定義行為且不希望被修改
virtual float Cost() const override final
{
return IBeverage_Ptr->Cost() + AddCost();
}
若沒有 final 這個關鍵字,就得用以下兩個方法:
- 道德勸說 IDecorator 的實作類別不要改寫
- IDecorator 不要實作,交給 IDecorator 的實作類別去寫,但這樣 IDecorator 要做什麼,且實作類別會怎麼做沒法控制
所以少了 final 就是不完美。
但這樣就完美了嗎,看看 main() 出現 new CCoffee,而 CCoffee 就直接由 IBeverage 延伸而來,若有人想擴展 CCoffee 勢必要用類別繼承,類別繼承至少有以下三點問題:
- 父子類別處於緊張狀態,父類別要修改時會顧忌會不會影響子類別
- 依 C++ 的標準,父類別若實作了某個要強制實作的純虛擬函數,子類別就不能再被要求要強制實作。
- 子類別有改寫虛擬函數的權力,父類別要面臨要不要使用 final 關鍵字的苦腦。
很幸運地,以上三點可以用 介面繼承+組合代替繼承 加以解決,以下重新改寫的範例:
#include <memory> #include <iostream> using namespace std; // Component (interface) // 飲料介面 class IBeverage { public: IBeverage() {} virtual ~IBeverage() {} // 回報費用 virtual float Cost() const = 0; }; // 介面繼承 // 咖啡介面 class ICoffee :public IBeverage { public: // 喝咖啡的動作 virtual void Drink() const = 0; inline static ICoffee *Create(); }; // ConcreteComponent // 咖啡的實作 class CCoffee :public ICoffee { // 回報費用 virtual float Cost() const override { return 10; } // 喝咖啡的動作 virtual void Drink() const override { cout << "咖啡好好喝" << endl; } public: CCoffee() {} ~CCoffee() {} }; ICoffee *ICoffee::Create() { // 為了簡化處理直接產生 CCoffee 實 // 例,實際上應該用工廠模式之類的方法產生 return new CCoffee; } // 介面繼承 class ITaiwanCoffee :public ICoffee { public: // 擴充的功能 virtual void Go() const = 0; inline static ITaiwanCoffee *Create(); }; // 實作繼承 class CTaiwanCoffee :public ITaiwanCoffee { // 用組合替代類別繼承 shared_ptr<ICoffee> ICoffee_Ptr; // 回報費用 virtual float Cost() const override { return 13; } // 喝咖啡的動作 virtual void Drink() const override { ICoffee_Ptr->Drink(); } virtual void Go() const override { cout << "Taiwan Go Go Go!!" << endl; } public: CTaiwanCoffee() :ICoffee_Ptr(ICoffee::Create()) {} ~CTaiwanCoffee(){} }; ITaiwanCoffee *ITaiwanCoffee::Create() { return new CTaiwanCoffee; } // Decorator (interface) // 裝飾者介面 class IDecorator :public IBeverage { // 包裹被裝飾的物件 shared_ptr<IBeverage> IBeverage_Ptr; // 子類別必須提供增加的費用 virtual float AddCost() const = 0; // 回報費用 // 重新定義行為且不希望被修改 virtual float Cost() const override final { return IBeverage_Ptr->Cost() + AddCost(); } public: IDecorator(const shared_ptr<IBeverage> &IBeverage_Arg) :IBeverage_Ptr(IBeverage_Arg) {} ~IDecorator() {} }; // 介面繼承 class IMocha :public IDecorator { protected: IMocha(const shared_ptr<IBeverage> &IBeverage_Arg) :IDecorator(IBeverage_Arg) {} public: inline static IMocha *Create(const shared_ptr<IBeverage> &IBeverage_Arg); }; // 實作繼承 class CMocha :public IMocha { // 子類別必須提供增加的費用 virtual float AddCost() const override { return 1.3f; } public: CMocha(const shared_ptr<IBeverage> &IBeverage_Arg) :IMocha(IBeverage_Arg) {} ~CMocha() {} }; IMocha *IMocha::Create(const shared_ptr<IBeverage> &IBeverage_Arg) { return new CMocha(IBeverage_Arg); } int main() { // 產生咖啡物件 // 並轉換使用場合所須要適當的角色介面 shared_ptr<ITaiwanCoffee> ITaiwanCoffee_Ptr(ITaiwanCoffee::Create()); shared_ptr<ICoffee> ICoffee_Ptr(ITaiwanCoffee_Ptr); shared_ptr<IBeverage> IBeverage_Ptr(ICoffee_Ptr); // 為咖啡附加 Mocha shared_ptr<IBeverage> Mocha1_Ptr(IMocha::Create(IBeverage_Ptr)); // 雙倍 Mocha 咖啡 shared_ptr<IBeverage> Mocha2_Ptr(IMocha::Create(Mocha1_Ptr)); cout << "雙倍 Mocha 咖啡的費用是:" << Mocha2_Ptr->Cost() << endl; // 執行擴增的個別功能 ICoffee_Ptr->Drink(); ITaiwanCoffee_Ptr->Go(); return 0; }
介面繼承+組合代替繼承 真的能完全取代類別繼承嗎?也就是說類別繼承能做到的 介面繼承+組合代替繼承 也能做到嗎?就目前來看是的,只是寫法要麻煩許多,範例中還未加上工廠模式,否則會更複雜。但卻能讓父子類別從強力耦合關係中解放出來,大大強化了彈性和日後的維謢
沒有留言:
張貼留言