C++設計模式——裝飾模式

前言

在實際開發時,你有沒有碰到過這種問題;開發一個類,封裝了一個對象的核心操做,而這些操做就是客戶使用該類時都會去調用的操做;而有一些非核心的操做,可能會使用,也可能不會使用;如今該怎麼辦呢?html

  1. 將這些非核心的操做所有放到類中,這樣,一個類就包含了不少核心的操做和一些看似有關,可是又無關的操做;這就會使核心類發生「爆炸」的現象,從而使核心類失去了必定的價值,也使使用核心類的客戶在覈心操做和非核心操做中掙扎;
  2. 使用繼承來擴展核心類,須要使用核心類時,直接創建核心類對象;當須要使用核心類擴展類時,就創建核心類擴展類對象;這樣貌似是一種頗有效的方法;可是因爲繼承爲類型引入的靜態特質,使得這種擴展方式缺少靈活性;同時,又掉入了另外一個陷阱,隨着擴展功能的增多,子類也會增多,各類子類的組合,就會致使類的膨脹,最後,就會被淹沒在類的海洋;此時,也不用我多說,你是否是想起了橋接模式,橋接模式就是爲了適應多個維度的變化而發生子類「爆炸」的狀況,可是,橋接模式是爲了適應抽象和實現的不一樣變化,並不適用於我這裏說的。那如何是好,這就要說到今天總結的裝飾模式了。

 

什麼是裝飾模式?

在GOF的《設計模式:可複用面向對象軟件的基礎》一書中對裝飾模式是這樣說的:動態地給一個對象添加一些額外的職責。就增長功能來講,Decorator模式相比生成子類更爲靈活。ios

裝飾模式可以實現動態的爲對象添加功能,是從一個對象外部來給對象添加功能。一般給對象添加功能,要麼直接修改對象添加相應的功能,要麼派生對應的子類來擴展,抑或是使用對象組合的方式。顯然,直接修改對應的類這種方式並不可取。在面向對象的設計中,而咱們也應該儘可能使用對象組合,而不是對象繼承來擴展和複用功能。裝飾器模式就是基於對象組合的方式,能夠很靈活的給對象添加所須要的功能。裝飾器模式的本質就是動態組合。動態是手段,組合纔是目的。總之,裝飾模式是經過把複雜的功能簡單化,分散化,而後再運行期間,根據須要來動態組合的這樣一個模式。它使得咱們能夠給某個對象而不是整個類添加一些功能。shell

 

UML類圖

Component:定義一個對象接口,能夠給這些對象動態地添加職責;windows

ConcreteComponent:定義一個具體的Component,繼承自Component,重寫了Component類的虛函數;設計模式

Decorator:維持一個指向Component對象的指針,該指針指向須要被裝飾的對象;並定義一個與Component接口一致的接口;函數

ConcreteDecorator:向組件添加職責。學習

 

代碼實現

 1 #include <iostream>
 2 using namespace std;  3 class Component  4 {  5 public:  6      virtual void Operation() = 0;  7 };  8 class ConcreteComponent : public Component  9 { 10 public: 11      void Operation() 12  { 13           cout<<"I am no decoratored ConcreteComponent"<<endl; 14  } 15 }; 16 class Decorator : public Component 17 { 18 public: 19      Decorator(Component *pComponent) : m_pComponentObj(pComponent) {} 20      void Operation() 21  { 22           if (m_pComponentObj != NULL) 23  { 24                m_pComponentObj->Operation(); 25  } 26  } 27 protected: 28      Component *m_pComponentObj; 29 }; 30 class ConcreteDecoratorA : public Decorator 31 { 32 public: 33      ConcreteDecoratorA(Component *pDecorator) : Decorator(pDecorator){} 34      void Operation() 35  { 36  AddedBehavior(); 37  Decorator::Operation(); 38  } 39      void AddedBehavior() 40  { 41           cout<<"This is added behavior A."<<endl; 42  } 43 }; 44 class ConcreteDecoratorB : public Decorator 45 { 46 public: 47      ConcreteDecoratorB(Component *pDecorator) : Decorator(pDecorator){} 48      void Operation() 49  { 50  AddedBehavior(); 51  Decorator::Operation(); 52  } 53      void AddedBehavior() 54  { 55           cout<<"This is added behavior B."<<endl; 56  } 57 }; 58 int main() 59 { 60      Component *pComponentObj = new ConcreteComponent(); 61      Decorator *pDecoratorAOjb = new ConcreteDecoratorA(pComponentObj); 62      pDecoratorAOjb->Operation(); 63      cout<<"============================================="<<endl; 64      Decorator *pDecoratorBOjb = new ConcreteDecoratorB(pComponentObj); 65      pDecoratorBOjb->Operation(); 66      cout<<"============================================="<<endl; 67      Decorator *pDecoratorBAOjb = new ConcreteDecoratorB(pDecoratorAOjb); 68      pDecoratorBAOjb->Operation(); 69      cout<<"============================================="<<endl; 70      delete pDecoratorBAOjb; 71      pDecoratorBAOjb = NULL; 72      delete pDecoratorBOjb; 73      pDecoratorBOjb = NULL; 74      delete pDecoratorAOjb; 75      pDecoratorAOjb = NULL; 76      delete pComponentObj; 77      pComponentObj = NULL; 78 }

 

使用場合

  1. 在不影響其餘對象的狀況下,以動態的,透明的方式給單個對象添加職責;
  2. 處理那些能夠撤銷的職責;
  3. 當不能採用生成子類的方法進行擴充時。一種狀況是,可能存在大量獨立的擴展,爲支持每一種組合將產生大量的子類,使得子類數目呈爆炸性增加。另外一種狀況多是由於類定義被隱藏,或類定義不能用於生成子類。

 

注意事項

  1. 接口的一致性;裝飾對象的接口必須與它所裝飾的Component的接口是一致的,所以,全部的ConcreteDecorator類必須有一個公共的父類;這樣對於用戶來講,就是統一的接口;
  2. 省略抽象的Decorator類;當僅須要添加一個職責時,沒有必要定義抽象Decorator類。由於咱們經常要處理,現存的類層次結構而不是設計一個新系統,這時能夠把Decorator向Component轉發請求的職責合併到ConcreteDecorator中;
  3. 保持Component類的簡單性;爲了保證接口的一致性,組件和裝飾必需要有一個公共的Component類,因此保持這個Component類的簡單性是很是重要的,因此,這個Component類應該集中於定義接口而不是存儲數據。對數據表示的定義應延遲到子類中,不然Component類會變得過於複雜和臃腫,於是難以大量使用。賦予Component類太多的功能,也使得具體的子類有一些它們它們不須要的功能大大增大;

 

實現要點

  1. Component類在Decorator模式中充當抽象接口的角色,不該該去實現具體的行爲。並且Decorator類對於Component類應該透明,換言之Component類無需知道Decorator類,Decorator類是從外部來擴展Component類的功能;
  2. Decorator類在接口上表現爲「is-a」Component的繼承關係,即Decorator類繼承了Component類所具備的接口。但在實現上又表現爲「has-a」Component的組合關係,即Decorator類又使用了另一個Component類。咱們可使用一個或者多個Decorator對象來「裝飾」一個Component對象,且裝飾後的對象仍然是一個Component對象;
  3. Decortor模式並不是解決「多子類衍生的多繼承」問題,Decorator模式的應用要點在於解決「主體類在多個方向上的擴展功能」——是爲「裝飾」的含義;
  4. 對於Decorator模式在實際中的運用能夠很靈活。若是隻有一個ConcreteComponent類而沒有抽象的Component類,那麼Decorator類能夠是ConcreteComponent的一個子類。若是隻有一個ConcreteDecorator類,那麼就沒有必要創建一個單獨的Decorator類,而能夠把Decorator和ConcreteDecorator的責任合併成一個類。
  5. Decorator模式的優勢是提供了比繼承更加靈活的擴展,經過使用不一樣的具體裝飾類以及這些裝飾類的排列組合,能夠創造出不少不一樣行爲的組合;
  6. 因爲使用裝飾模式,能夠比使用繼承關係須要較少數目的類。使用較少的類,固然使設計比較易於進行。可是,在另外一方面,使用裝飾模式會產生比使用繼承關係更多的對象。更多的對象會使得查錯變得困難,特別是這些對象看上去都很相像。

 

與橋接模式的區別

以前總結了C++設計模式——橋接模式;你會發現,兩者都是爲了防止過分的繼承,從而形成子類氾濫的狀況。那麼兩者之間的主要區別是什麼呢?橋接模式的定義是將抽象化與實現化分離(用組合的方式而不是繼承的方式),使得二者能夠獨立變化。能夠減小派生類的增加。若是光從這一點來看的話,和裝飾者差很少,但二者仍是有一些比較重要的區別:spa

  1. 橋接模式中所說的分離,實際上是指將結構與實現分離(當結構和實現有可能發生變化時)或屬性與基於屬性的行爲進行分離;而裝飾者只是對基於屬性的行爲進行封閉成獨立的類,從而達到對其進行裝飾,也就是擴展。好比:異常類和異常處理類之間就可使用橋接模式來實現完成,而不能使用裝飾模式來進行設計;若是對於異常的處理須要進行擴展時,咱們又能夠對異常處理類添加Decorator,從而添加處理的裝飾,達到異常處理的擴展,這就是一個橋接模式與裝飾模式的搭配;
  2. 橋接中的行爲是橫向的行爲,行爲彼此之間無關聯,注意這裏的行爲之間是沒有關聯的,就好比異常和異常處理之間是沒有行爲關聯的同樣;而裝飾者模式中的行爲具備可疊加性,其表現出來的結果是一個總體,一個各個行爲組合後的一個結果。

 

總結

裝飾模式重點在裝飾,對核心功能的裝飾做用;將繼承中對子類的擴輾轉化爲功能類的組合,從而將須要對子類的擴輾轉嫁給用戶去進行調用組合,用戶如何組合由用戶去決定。我在學習裝飾模式時,就是重點分析了「裝飾」這個詞,咱們都知道,裝飾是在一個核心功能上添加一些附屬功能,從而讓核心功能發揮更大的做用,可是最終它的核心功能是不能丟失的。這就比如咱們進行windows shell開發時,咱們是對windows的這層殼進行了功能的裝飾,從而實現了咱們須要的一些裝飾功能,可是最終的功能仍是由windows shell去完成。這就比如,咱們的裝飾就是給核心功能添加了一層外衣,讓它看起來更漂亮和完美。設計

相關文章
相關標籤/搜索