Decorator模式——設計模式學習筆記


Decorator模式

一 意圖

  動態地給一個對象添加一些額外的職責。就增加功能來說,Decorator模式相比生成子類更為靈活。——包裝器Wrapper。

二 動機

  有時我們希望給某個對象而不是整個類添加一些功能。

例如,一個圖形用戶界面工具箱允許你對任意一個用戶界面組件添加一些特性,例如邊框,或是一些行為,例如窗口滾動。

使用繼承機制是添加功能的一種有效途徑,從其他類繼承過來的邊框特性可以被多個子類的實例所使用。

但這種方法不夠靈活,因為邊框的選擇是靜態的,用戶不能控制對組件加邊框的方式和時機。

  一種較為靈活的方式是將組件嵌入另一個對象中,由這個對象添加邊框。我們稱這個嵌入的對象為裝飾。

這個裝飾與它所裝飾的組件接口一致,因此它對使用該組件的客戶透明。它將客戶請求轉發給該組件,並且

可能在轉發前后執行一些額外的動作(例如畫一個邊框)。

透明性使得你可以遞歸的嵌套多個裝飾,從而可以添加任意多的功能,如下圖所示。

      


這樣一種結構:

      


VisualComponent是一個描述可視對象的抽象類,它定義了繪制和事件處理的接口。

注意Decorator類怎樣將繪制請求簡單地發送給它的組件,以及Decorator的子類如何擴展這個操作。

  Decorator的子類為特定功能可以自由地添加一些操作。

例如,如果其他對象知道界面中恰好有一個ScrollDecorator對象,這些對象就可以用ScrollDecorator對象的ScrollTo操作滾動這個界面。

這個模式中有一點很重要,它使得在VisualComponent可以出現的任何地方都可以有裝飾。

因此,客戶通常不會感覺到裝飾過的組件與未裝飾組件之間的差異,也不會與裝飾產生任何依賴關系。


Decorator中存在一個Component的對象,來接收所要裝飾的組件——被裝飾。

 

繼承結構來實現:

或者組合的方式:


  對於這種情況,這三種方式中:我覺得使用Deccorator或許並不合適,Decorator讓對象被裝飾,

將對象傳遞給可以裝飾的裝飾對象,得到的將是一個被裝飾后的對象。不是對象本身。

雖然可以通過Decorator傳遞請求,但勢必讓事情變得更麻煩。故通常我們仍然需要操作的是原對象本身。

這樣一來需要維護兩個對象……麻煩! Why???


三 適用性以及其結構

       以下情況使用Decorator模式

1.在不影響其他對象的情況下,以動態透明的方式給單個對象添加職責。

2 處理那些可以撤消的職責。

3.當不能采用生成子類的方法進行擴充時。一種情況是,可能有大量獨立的擴展,

為支持每一種組合將產生大量的子類,使得子類數目呈爆炸性增長。另一種情況可能是因為類定義被隱藏,或類定義不能用於生成子類。


結構:

    


Component ( VisualComponent )

定義一個對象接口,可以給這些對象動態地添加職責。

• ConcreteComponent ( TextView )

定義一個對象,可以給這個對象添加一些職責。

• Decorator

維持一個指向Component對象的指針,並定義一個與Component接口一致的接口。

• ConcreteDecorator(BoaderDecorator,ScrollDecorator)

向組件添加職責。

•Decorator將請求轉發給它的Component對象,並有可能在轉發請求前后執行一些附加的動作。

 

四 效果分析


1 比靜態繼承更靈活

       可以用添加和分離的方法,用裝飾在運行時刻增加和刪除職責。

而繼承機制要求為每個添加的職責創建一個新的子類。更靈活的添加裝飾:

使用Decorator模式可以很容易地重復添加一個特性,例如在TextView上添加雙邊框時,

僅需將添加兩個BoarderDecorator即可。而兩次繼承Boarder類則極容易出錯的。


2 避免在層次結構高層的類有太多的特征

       Decorator模式提供了一種“即用即付”的方法來添加職責。可以通過定制來逐步的構造復雜的對象。

五 代碼實現

1 Decorator

View Code
#include <iostream.h>
#include <memory.h>

/*----------------------------------------------------------------*/
/* class VisualComponent */
/*----------------------------------------------------------------*/
class VisualComponent
{
public:
VisualComponent(){}
virtual void Draw() = 0;
};

/*----------------------------------------------------------------*/
/* class TextView */
/*----------------------------------------------------------------*/
class TextViewControl : public VisualComponent
{
#define CONTENT_LEN 100
public:
TextViewControl(char* str, int len)
{
memset(m_content,0,CONTENT_LEN);
memcpy(m_content,str,len);
}
virtual void Draw()
{
cout<<" TextViewControl Draw content = "<<m_content<<endl;
}
private:
char m_content[CONTENT_LEN];
};

/*----------------------------------------------------------------*/
/* class Decorator */
/*----------------------------------------------------------------*/
class Decorator: public VisualComponent
{
public:
Decorator(VisualComponent* component)
{
m_componnet = component;
}
virtual void Draw()
{
m_componnet->Draw();
cout<<" I am Decorator Draw"<<endl;
}
private:
VisualComponent* m_componnet;
};

/*----------------------------------------------------------------*/
/* class BoarderDecorator */
/*----------------------------------------------------------------*/
class BoarderDecorator: public Decorator
{
public:
BoarderDecorator(VisualComponent* component, int boarderWidth):Decorator(component)
{
m_boarderWidth = boarderWidth;
}
virtual void Draw()
{
Decorator::Draw();
cout<<" I am BoarderDecorator Draw"<<endl;

this->DrawBorder();
}
protected:
void DrawBorder()
{
cout<<" DrawBorder m_boarderWidth = "<<m_boarderWidth<<endl;
}
private:
int m_boarderWidth;
};

/*----------------------------------------------------------------*/
/* class ScrollDecorator */
/*----------------------------------------------------------------*/
class ScrollDecorator: public Decorator
{
public:
ScrollDecorator(VisualComponent* component, int scrollHeight):Decorator(component)
{
m_scrollHeight = scrollHeight;
}
virtual void Draw()
{
Decorator::Draw();
cout<<" I am ScrollDecorator "<<endl;

this->DrawScroll();
}
protected:
void DrawScroll()
{
cout<<" DrawScroll m_scrollHeight = "<<m_scrollHeight<<endl;
}
private:
int m_scrollHeight;
};

2 Test

View Code
#include "Decorator.h"
#include <string.h>

int main()
{
char str[] = "TextViewControl";

TextViewControl* textView = new TextViewControl(str,strlen(str));
ScrollDecorator* scroll = new ScrollDecorator(textView,100);
BoarderDecorator* board = new BoarderDecorator(scroll,200);

board->Draw();

return 0;
}

3 Result

View Code
 TextViewControl Draw con
I am Decorator
I am ScrollDecorator
m_scrollHeight = 100
I am Decorator
I am BoarderDecorator
m_boarderWidth = 200

 

六 實例分析

         Decorator裝飾或者Wrapper包裝這個些詞太容易讓人想到的是圖形用戶界面,這些控件方面的應用了。但這僅僅只是一種形象的表達而已,不局限於此。

1 For Example:  

  Streams是大多數I / O設備的基礎抽象結構,它提供了將對象轉換成為字節或字符流的操作接口,

使我們可以將一個對象轉變成一個文件或內存中的字符串,可以在以后恢復使用。一個簡單直接的

方法是定義一個抽象的Stream類,它有兩個子類MemoryStream與FileStream。但假定我們還希望能夠做下面一些事情:

• 用不同的壓縮算法(行程編碼, Lempel-Ziv等)對數據流進行壓縮。

• 將流數據簡化為7位A S C I I碼字符,這樣它就可以在A S C I I信道上傳輸。

Decorator模式提供的將這些功能添加到Stream中方法很巧妙。下面的類圖給出了一個解決問題的方法。

    



  Stream抽象類維持了一個內部緩沖區並提供一些操作( PutInt, PutString)用於將數據存入流中。

一旦這個緩沖區滿了,Stream就會調用抽象操作HandleBufferFull進行實際數據傳輸。

在FileStream中重定義了這個操作,將緩沖區中的數據傳輸到文件中去。

  這里的關鍵類是StreamDecorator,它維持了一個指向組件流的指針並將請求轉發給它,

StreamDecorator子類重定義HandleBufferFull 操作並且在調用StreamDecorator的HandleBufferFull操作之前執行一些額外的動作。

  例如,CompressingStream子類用於壓縮數據,而ASCII7Stream將數據轉換成7位ASCII碼。

現在我們創建FileStream類,它首先將數據壓縮,然后將壓縮了的二進制數據轉換成為7位ASCII碼,

我們用CompressingStream和ASCII7Stream裝飾FileStream:

  

Stream* aStream = new ASCII7Stream (
new CompressingStream (
new FileStream("aFileStream")
)
);
aStream->PutInt(12);
aStream->PutString("aString");

 

2 相關模式:

  1. Adapter模式:Decorator模式不同於Adapter模式,因為裝飾僅改變對象的職責而不改變它的接口;而適配器將給對象一個全新的接口。
  2. Composite模式:可以將裝飾視為一個退化的、僅有一個組件的組合。然而,裝飾僅給對象添加一些額外的職責—它的目的不在於對象聚集。
  3. Strategy模式:用一個裝飾你可以改變對象的外表;而Strategy模式使得你可以改變對象的內核。這是改變對象的兩種途徑。

 

通過幾個例子到這里之后,我一直在慢慢覺得,這個模式不錯感覺很好!

竟然有一種無法表達的感覺……他能夠把你想干的幾件相關的事情分開來處理,讓事情變得清晰明了。。。




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM