c++設計模式----裝飾模式

前言

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

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

什麼是裝飾模式?

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

UML類圖

  

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

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

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

ConcreteDecorator:向組件添加職責。指針

 

代碼實現:code

#include <iostream>
using namespace std;
class Component
{
public:
     virtual void Operation() = 0;
};
class ConcreteComponent : public Component
{
public:
    ConcreteComponent()
    {
        cout <<" ConcreteComponent "<< endl;
    }
     void Operation()
     {
          cout<<"I am no decoratored ConcreteComponent"<<endl;
     }
};
class Decorator : public Component
{
public:
     Decorator(Component *pComponent) : m_pComponentObj(pComponent) 
     {
         cout << " Decorator "<<endl;
     }
     void Operation()
     {
          if (m_pComponentObj != NULL)
          {
               m_pComponentObj->Operation();
          }
     }
protected:
     Component *m_pComponentObj;
};
class ConcreteDecoratorA : public Decorator
{
public:
     ConcreteDecoratorA(Component *pDecorator) : Decorator(pDecorator)
     {
         cout << " ConcreteDecoratorA "<<endl;
     }
     void Operation()
     {
          AddedBehavior();
          Decorator::Operation();
     }
     void  AddedBehavior()
     {
          cout<<"This is added behavior A."<<endl;
     }
};
class ConcreteDecoratorB : public Decorator
{
public:
     ConcreteDecoratorB(Component *pDecorator) : Decorator(pDecorator)
     {
         cout << " ConcreteDecoratorB "<<endl;
     }
     void Operation()
     {
          AddedBehavior();
          Decorator::Operation();
     }
     void  AddedBehavior()
     {
          cout<<"This is added behavior B."<<endl;
     }
};
int main()
{
     Component *pComponentObj = new ConcreteComponent();
     Decorator *pDecoratorAOjb = new ConcreteDecoratorA(pComponentObj);
     pDecoratorAOjb->Operation();
     cout<<"============================================="<<endl;
     Decorator *pDecoratorBOjb = new ConcreteDecoratorB(pComponentObj);
     pDecoratorBOjb->Operation();
     cout<<"============================================="<<endl;
     Decorator *pDecoratorBAOjb = new ConcreteDecoratorB(pDecoratorAOjb);
     pDecoratorBAOjb->Operation();
     cout<<"============================================="<<endl;
     delete pDecoratorBAOjb;
     pDecoratorBAOjb = NULL;
     delete pDecoratorBOjb;
     pDecoratorBOjb = NULL;
     delete pDecoratorAOjb;
     pDecoratorAOjb = NULL;
     delete pComponentObj;
     pComponentObj = NULL;
}

 使用場合

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

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

 

轉自:C++設計模式——裝飾模式  blog

相關文章
相關標籤/搜索