裝飾器模式是比較常用的一種設計模式,Python中就內置了對於裝飾器的支持。
具體來說,裝飾器模式是用來給對象增加某些特性或者對被裝飾對象進行某些修改。

如上圖所示,需要被裝飾的對象在最上方,它自身可以有自己的實例,一般通過抽象類來實現(Java中也可以通過接口實現)。
右側中間是一個裝飾器類或者接口,其實內容與原對象基本一致,不過我們自定義的裝飾器一般會繼承這個裝飾器基類。
最下層就是具體的裝飾器了,可以看到,具體裝飾器類中需要包含被裝飾對象成員(也就是說,裝飾器需要和被裝飾對象有同樣的子類),然后增加一些額外的操作。
下面的代碼是一個買煎餅的例子,如我們生活中所見,可以選基礎煎餅(雞蛋煎餅,肉煎餅等),然后再額外加別的東西:
1 #include<iostream> 2 #include<string> 3 using namespace std; 4 5 class Pancake//基類 6 { 7 public: 8 string description = "Basic Pancake"; 9 virtual string getDescription(){ return description; } 10 virtual double cost() = 0; 11 }; 12 13 class CondimentDecorator :public Pancake//裝飾器基類 14 { 15 public: 16 string getDescrition(); 17 }; 18 19 class MeatPancake :public Pancake//肉煎餅 20 { 21 public: 22 MeatPancake(){ description = "MeatPancake"; } 23 double cost(){ return 6; } 24 }; 25 class EggPancake :public Pancake//雞蛋煎餅 26 { 27 public: 28 EggPancake(){ description = "EggPancake"; } 29 double cost(){ return 5; } 30 }; 31 32 class Egg :public CondimentDecorator//額外加雞蛋 33 { 34 public: 35 Pancake* base; 36 string getDescription(){ return base->getDescription() + ", Egg"; } 37 Egg(Pancake* d){ base = d; } 38 double cost(){ return base->cost() + 1.5; } 39 }; 40 class Potato :public CondimentDecorator//額外加土豆 41 { 42 public: 43 Pancake* base; 44 string getDescription(){ return base->getDescription() + ", Potato"; } 45 Potato(Pancake* d){ base = d; } 46 double cost(){ return base->cost() + 1; } 47 }; 48 class Bacon :public CondimentDecorator//額外加培根 49 { 50 public: 51 Pancake* base; 52 string getDescription(){ return base->getDescription() + ", Bacon"; } 53 Bacon(Pancake* d){ base = d; } 54 double cost(){ return base->cost() + 2; } 55 }; 56 57 58 int main() 59 { 60 Pancake* pan = new EggPancake(); 61 pan = &Potato(pan); 62 pan = &Bacon(pan); 63 cout << pan->getDescription() << " $ : " << pan->cost() << endl; 64 system("pause"); 65 return 0; 66 }
可以看到,Pancake是煎餅的基類,這個基類因為定義了純虛函數cost,所以不能被實例化。同樣,裝飾器基類CondimentDecorator也不能被實例化。
后面,EggPancake和MeatPancake是Pancake的派生類,是實際存在的類。
Egg、Bacon和Potato就是我們定義的裝飾器對象,其中包含了Pancake的指針,可以對Pancake及其派生類進行操作。
運行代碼的結果如下:

即我們選擇了一個雞蛋煎餅,然后額外加了土豆和培根,一共5+1+2=8塊。
裝飾器模式的優點:
1、可以輕松對已存在的對象進行修改和包裝,在被裝飾者的前面或者后面添加自己的行為,而無需修改原對象。
2、可以動態、不限量地進行裝飾,可以更靈活地擴展功能。
相對地,裝飾器模式有很明顯的缺點:
1、會加入大量的小類,即使只添加一個功能,也要額外創建一個類,使得程序更復雜。
2、增加代碼復雜度,使用裝飾器模式不但需要實例化組件,還要把組件包裝到裝飾者中。
