設計模式(六)裝飾模式(轉)

原文地址:http://www.jellythink.com/archives/171ios

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

前言

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

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

什麼是裝飾模式?

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

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

UML類圖

果凍想 | 一個原創文章分享網站

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

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

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

ConcreteDecorator:向組件添加職責。網站

代碼實現

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

使用場合

  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去完成。這就比如,咱們的裝飾就是給核心功能添加了一層外衣,讓它看起來更漂亮和完美。設計

相關文章
相關標籤/搜索