程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++設計模式之裝飾模式講解

C++設計模式之裝飾模式講解

編輯:C++入門知識

C++設計模式之裝飾模式講解


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

動態地給一個對象增加一些額外的職責,就增加對象的功能來說,裝飾模式比生成子類更為靈活。裝飾模式是一種對象結構模式。

一、緣由

我們常常通過繼承的方式來對一個既有的類進行功能添加,但繼承方式有顯著的局限性,因為

繼承具有侵入性繼承是一種is a的關系,具有強耦合性,難以復用代碼。

例如在窗口控件當中,要增加新的功能如增加滾動條,增加背景圖片,通過繼承的方式來增加新的功能,有下面的解決方案。

可以看到在這個解決方案中,我們分別將ScrollBckGnd的功能實現了三遍。其根本原因是繼承是一種強耦合的關系,我們無法把BckGndTextBox中的BckGnd部分拿出來給ListBox使用。於是我們期待一種模式,可以像繼承這樣方便地增加新的功能,並且能夠方便地復用代碼,這種模式真實存在—裝飾模式

二、實現

其實裝飾模式的設計理念很簡單,就是利用聚合將要被添加功能的類聚合到用於添加功能的類的內部,正如裝飾模式的名字一樣,用一個新的類將被裝飾的類包裝起來
假設我們將需要被裝飾(添加功能)的類稱作Component而用於裝飾(添加功能)的類稱作Decorator。這樣,我們需要在Decorator內部聚合一個Component,然後再將新的功能和舊的功能組合成為一個調用鏈,現在代碼大致如下:

class Component {
public:
  Component (){};
  virtual ~Component ();
  virtual void operate(){};
};

class Decorator {
private:
  Component* component;
  void addOperate():
public:
  Decorator (Component* c):component(c){};
  virtual ~Decorator ();
  virtual void operate(){
      addOperate();
      component->operate()
  }
};

現在我們已經為Component類添加了新的功能,可是這樣做問題很明顯,添加了新功能之後的Decorator根本無法用在那些原本使用Component的地方。OK那麼我們就讓Decorator成為一個Component類,這樣就可以解決這個問題了。現在代碼修改如下:

class Component {
public:
  Component (){};
  virtual ~Component ();
  virtual void operate(){};
};

class Decorator : public Component{
private:
  Component* component;
  void addOperate():
public:
  Decorator (Component* c):component(c){};
  virtual ~Decorator ();
  virtual void operate() override{
      addOperate();
      component->operate()
  }
};

int main()
{
    Component *A = new Component;
    Component *B = new Decorator(A);
    Component *C = new Decorator(B);
    Component *D = new Decorator(C);
    Component *E = new Decorator(D);
    Component *F = new Decorator(E);

    F->operate();
}

可是問題又出現了,我們如何靈活地改變需要添加的新功能呢?也就是addOperate?我們可以將addOperate寫成一個虛函數,將這個職責推遲到子類當中去。修改後的代碼如下(代碼1):

class Component {
public:
  Component (){};
  virtual ~Component (){};
  virtual void operate(){
      std::cout << "Component::operate" << std::endl;
  };
};

class Decorator : public Component{
private:
    Component* component;
protected:
  virtual void addOperate(){};
public:
  Decorator (Component* c):component(c){};
  virtual ~Decorator (){};
  virtual void operate() override{
      addOperate();
      component->operate();
  }
};

class DecoratorB : public Decorator{
protected:
  virtual void addOperate() override{
      std::cout << "DecoratorB::addOperate" << std::endl;
  };
public:
  DecoratorB (Component* c):Decorator(c){};
  virtual ~DecoratorB (){};
};

class DecoratorA : public Decorator{
protected:
  virtual void addOperate() override{
      std::cout << "DecoratorA::addOperate" << std::endl;
  };  
public:
  DecoratorA (Component* c):Decorator(c){};
  virtual ~DecoratorA (){};
};

int main()
{
    Component *A = new Component;
    Component *B = new DecoratorA(A);
    Component *C = new DecoratorB(B);
    Component *D = new DecoratorA(C);
    Component *E = new DecoratorB(D);
    Component *F = new DecoratorA(E);

    F->operate();
}

運行結果:
DecoratorA::addOperate
DecoratorB::addOperate
DecoratorA::addOperate
DecoratorB::addOperate
DecoratorA::addOperate
Component::operate

這時總歸不方便,因為父類Decorator中規定了接口addOperate,子類不想使用這個接口作為添加的新功能都不行,假設子類要添加一個addStateaddCounter之類的功能呢?子類必須實現addStateaddCounter然後利用addOperate調用這兩個方法,這樣的做法有點繞,干脆將addOperate的聲明也一並推遲到子類中讓子類來決定這個接口該是什麼,但是推遲了addOperate的聲明,必然也會推遲對其的調用,這個調用依舊放在virtual void operate中。修改後的代碼如下(代碼2):

class Component {
public:
    Component (){};
    virtual ~Component (){};
    virtual void operate(){
        std::cout << "Component::operate" << std::endl;
    }
};

class Decorator : public Component{
private:
    Component *_component;
public:
    Decorator (Component *C):_component(C){}
    virtual ~Decorator (){delete _component;}
    virtual void operate() override {
        _component->operate();
    }
};

class DecoratorA : public Decorator{
private:
    void addBehave(){
        std::cout << "DecoratorA:addBehave" << std::endl;
    }
public:
    DecoratorA(Component *D):Decorator(D){}
    virtual void operate () override{
        Decorator::operate();
        addBehave();
    }
};

class DecoratorB : public Decorator{
private:
    void addBehave(){
        std::cout << "DecoratorB:addBehave" << std::endl;
    }
public:
    DecoratorB(Component *D):Decorator(D){}
    virtual void operate () override{
        Decorator::operate();
        addBehave();
    }
};

以上的代碼就是裝飾模式的示例代碼。代碼2就是“正統”的裝飾模式實現,但我個人更喜歡代碼1中的實現,因為在增加新的功能的時候,不需要修改operate方法,這個向外提供的方法,早早地固定好了,子類越少改動它,出錯的幾率就越小。

三、UML類圖

下面給出裝飾模式的類圖和Widget的整體方案

四、總結

裝飾模式的優待點:

可以拓展一個對象的功能而不用繼承,十分方便靈活,不會造成類的個數大量增加可以動態拓展對象的功能具體的構件類和裝飾類可以獨立變化,增加新的功能的時候不需要修改既有的類,符合開閉原則。

裝飾模式的缺點:

邏輯上比繼承復雜,出錯可能性比繼承大

裝飾模式的適用場景:

需要動態拓展對象的功能時不能使用繼承來增加新功能時    

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved