[學習筆記]設計模式之Decorator

寫在前面

爲方便讀者,本文已添加至索引html

Decorator(裝飾)模式,能夠動態地給一個對象添加一些額外的職能。爲了更好地理解這個模式,咱們將時間線拉回Bridge模式筆記的結尾。那時,白雪公主射出了充滿魔法力量的一箭。如好萊塢大片通常,那支飛出的箭矢散發出各類你能想到的美麗光芒。固然,我不會告訴你那支箭華華麗麗地射偏了。由於咱們此次更關心這絢爛的魔法效果。要說的是,時の魔導士在創建這個平行世界的時候,定義了一個全部可見物體的抽象類VisualObject設計模式

1 class VisualObject {
2 public:    
3     virtual void show() = 0;    
4     // ... other ...
5 }

只要是出如今這個世界中,並能被世界以外的人們(好比說咱們)看到的物體,都是它的子類。同時,必須實現show()方法。show定義了這個物體呈現給觀衆的樣式。固然咱們能夠看到時の魔導士在建立箭矢時是如何偷懶的:學習

1 class Arrow : public VisualObject {
2 public:    
3     virtual void show() { cout << "你想它是什麼樣就是什麼樣。" << endl; }
4     // ... other ...
5 }

固然,這是不可行的。由於確定有人會把Arrow想像成一個巨大的炮彈,甚至一頭會飛的豬,以此來加強整個故事的動畫性。但不管如何,當魔導士想要告訴你們,公主射出的箭矢是附着着冰霜效果的話,他必須明確地描述這支箭矢到底長什麼樣。若是偷懶到底,他大能夠就創造個Arrow的子類IceArrow,只需多加個關於冰晶的描述就行了。動畫

可是問題來了,既然能夠附着魔法,箭矢就能夠有更多更多的效果:附着火焰的,附着閃電的,長得更好看的,等等等等……若是每個都用子類的話,必然會致使子類數目太多。何況,咱們僅僅是但願給某個對象而不是整個類添加一些特效。同時別忘了咱們還要考慮箭矢以外的能夠附魔的東西。怎麼辦纔好?時の魔導士採用了Decorator模式。spa

要點梳理

  • 目的分類
    • 對象結構型模式
  • 範圍準則
    • 對象(該模式處理對象間的關係,這些關係在運行時刻是能夠變化的,更具動態性)
  • 主要功能
    • 動態地給一個對象添加一些額外的職責。就增長功能來講, 它相比生成子類更爲靈活。
  • 適用狀況
    • 在不影響其餘對象的狀況下,以動態、透明的方式給單個對象添加職責
    • 處理那些能夠撤消的職責
    • 當不能採用生成子類的方法進行擴充時。例如,可能有大量獨立的擴展,爲支持每一種組合將產生大量的子類,使得子類數目呈爆炸性增加
  • 參與部分
    • Component:定義一個對象接口,能夠給這些對象動態地添加職責
    • ConcreteComponent:定義一個對象,能夠給這個對象添加一些職責
    • Decorator:維持一個指向Component對象的指針,並定義一個與Component接口一致的接口
    • ConcreteDecorator:向組件添加職能
  • 協做過程
    • Decorator將請求轉發給它的Component對象,並有可能在轉發請求先後執行一些附加的動做
  • UML圖

示例分析 - 宛如大片中的魔法箭矢

請不要太在乎上面這幅圖究竟是誰。爲了能給箭矢(乃至更多物體)增長魔法效果,魔導士引入了ObjectDecorator類:設計

 1 class ObjectDecorator : public VisualObject {
 2 public:
 3     ObjectDecorator(VisualObject *);
 4     
 5     virtual void show();
 6     // ... other ...
 7 private:
 8     VisualObject* _component;
 9 }
10 
11 void ObjectDecorator::show() {
12     _component->show();
13 }

ObjectDecorator裝飾由_component實例變量引用的VisualObject,這個變量在構造器中被初始化。同時,ObjectDecorator將show的請求轉發給_component。咱們將看到ObjectDecorator的子類之一MagicDecorator,它將能給可視化物件添加魔法的神奇效果:指針

 1 class MagicDecorator : public ObjectDecorator {
 2 public:
 3     MagicDecorator(VisualObject *, int);
 4     virtual void show();
 5     // ... other ...
 6 private:
 7     int _magicType;    
 8     
 9     void addMagic(int);
10 }
11 
12 void MagicDecorator::show() {
13     ObjectDecorator::show();
14     addMagic(_magicType);
15 }

相似的,咱們也能夠實現別的裝飾類。咱們接下來要把這個箭矢放到這個世界中去。假設World類(一個單例類)已經爲此提供了一個add2Me操做:code

1 void World::add2Me(VisualObject* obj) {
2     // ...
3     obj->show();
4     // ...
5 }

如今咱們要讓身處世界以外的人也看到一根箭矢,只需這麼作:component

1 Arrow* arrow = new Arrow();
2 World::getInstance()->add2Me(arrow);

那麼此刻,正是展示電影特效各類神奇絢麗效果的時刻了。咱們在將箭矢放入世界以前,先給它加上一層寒冰魔法的裝飾:htm

1 #define ICEMAGIC 1
2 World::getInstance()->add2Me(new MagicDecorator(arrow, ICEMAGIC));

OK,白雪公主射出了一支帶有寒冰魔法的箭矢。下面的UML圖說明了咱們這個例子:

特色總結

能夠看到Decorator裝飾模式有如下2個主要優勢和缺點:

  1. 比靜態繼承更靈活。它提供了更加靈活的向對象添加職能的方式,能夠用添加和分離的方法,用裝飾在運行時刻增長和刪除職能。同時使用Decorator模式能夠很容易地重複添加一個特性,好比讓箭矢附上冰與火雙重魔法。
  2. 避免在層次結構高層的類有太多的特徵。它並不試圖在一個複雜的可定製的類中支持全部可預見的特徵,相反,咱們能夠定義一個簡單的類,而且用Decorator類給它逐漸地添加功能。
  3. Decorator與它的Component不同。若是咱們從對象標識的觀點出發,一個被裝飾了的組件與這個組件是有差異的,所以,使用裝飾時不該該依賴對象標識。
  4. 有許多小對象。採用Decorator模式進行系統設計每每會產生許多看上去相似的小對象,這些對象僅僅在他們相互鏈接的方式上有所不一樣,而不是它們的類或是它們的屬性值有所不一樣。儘管對於那些瞭解這些系統的人來講,很容易對它們進行定製,可是很難學習這些系統,排錯也很困難。

一樣,咱們在使用這個方法時,也要注意:

  1. 接口的一致性。裝飾對象的接口必須與它所裝飾的Component的接口是一致的,所以,全部的ConcreteDecorator類必須有一個公共的父類(至少在C++中如此)。
  2. 保持Component類的簡單性。爲了保證接口的一致性,組件和裝飾必須有一個公共的Component父類,所以保持這個類的簡單性是很重要的。它應集中於定義接口而不是存儲數據。不然Component類會變得過於複雜和龐大,於是難以大量使用
  3. 省略抽象的Decorator類。當咱們僅須要添加一個職責時,沒有必要定義抽象Decorator類。咱們經常須要處理現存的類層次結構而不是設計一個新系統,這時咱們能夠把Decorator向Component轉發請求的職責合併到ConcreteDecorator中。

寫在最後

今天的筆記就到這裏了,歡迎你們批評指正!若是以爲能夠的話,好文推薦一下,我會很是感謝的!

相關文章
相關標籤/搜索