[設計模式] 9 裝飾者模式 Decorator

轉:http://www.jellythink.com/archives/171#prettyPhoto

什麼是裝飾模式?

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

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

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

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

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

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

/*
** FileName     : DecoratorPatternDemo
** Author       : Jelly Young
** Date         : 2013/12/19
** Description  : More information, please go to http://www.jellythink.com
*/
#include <iostream>
using namespace std;
class Component
{
public:
     virtual void Operation() = 0;
};
class ConcreteComponent : public Component
{
public:
     void Operation()
     {
          cout<<"I am no decoratored ConcreteComponent"<<endl;
     }
};
class Decorator : public Component
{
public:
     Decorator(Component *pComponent) : m_pComponentObj(pComponent) {}
     void Operation()
     {
          if (m_pComponentObj != NULL)
          {
               m_pComponentObj->Operation();
          }
     }
protected:
     Component *m_pComponentObj;
};
class ConcreteDecoratorA : public Decorator
{
public:
     ConcreteDecoratorA(Component *pDecorator) : Decorator(pDecorator){}
     void Operation()
     {
          AddedBehavior();
          Decorator::Operation();
     }
     void  AddedBehavior()
     {
          cout<<"This is added behavior A."<<endl;
     }
};
class ConcreteDecoratorB : public Decorator
{
public:
     ConcreteDecoratorB(Component *pDecorator) : Decorator(pDecorator){}
     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++設計模式——橋接模式;你會發現,兩者都是爲了防止過分的繼承,從而形成子類氾濫的狀況。那麼兩者之間的主要區別是什麼呢?橋接模式的定義是將抽象化與實現化分離(用組合的方式而不是繼承的方式),使得二者能夠獨立變化。能夠減小派生類的增加。若是光從這一點來看的話,和裝飾者差很少,但二者仍是有一些比較重要的區別:spa

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

總結

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

相關文章
相關標籤/搜索