天天看點

DECORATOR 裝飾模式

DECORATOR  裝飾模式   對象結構性模式

1、意圖

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

2、别名

包裝器Wrapper

3、動機

        有時我們希望給某對象而不是整個類添加一些功能。例如,一個圖形使用者界面工具箱允許你對任意一個使用者界面元件添加一些特性,例如邊框,或是一些行為,例如視窗滾動。

        使用繼承機制是添加功能的一種有效途徑,從其它類繼承過來的邊框特性可以被多個子類的執行個體所使用。但這種方法不夠靈活,因為邊框的選擇是靜态的,使用者不能控制對元件加邊框的方式和時機。

        一種較為靈活的方式是将元件嵌入另一個對象中,由這個對象添加邊框。我們成這個嵌入的對象為裝飾。這個裝飾與它所裝飾的元件接口一緻,是以它對使用該元件的客戶透明。它将客戶請求轉發給該元件,并且可能在轉發前後執行一些額外的動作(例如畫一個邊框)。透明性使得你可以遞歸的嵌套多個裝飾,進而可以添加任意多的功能,如下圖所示。

DECORATOR 裝飾模式

        例如,假定有一個對象TextView,它可以在視窗中顯示正文。預設的TextView沒有滾動條,因為我們可能有時并不需要滾動條。當需要滾動條時,我們可以用ScrollDecorator添加滾動條。如果我們還想在TextView周圍添加一個粗黑邊框,我們可以使用BorderDecorator添加。是以隻要簡單地将這些裝飾和TextView進行組合,就可以達到預期的效果。

        下面的對象圖展示了如何将一個TextView對象與BorderDecorator以及ScrollDecorator對象組裝起來産生一個具有邊框和滾動條的文本顯示視窗。

DECORATOR 裝飾模式

        ScrollDecorator和BorderDecorator類是Decorator類的子類。Decorator類是一個可視元件的抽象類,用于裝飾其它可視元件,如下圖所示。

DECORATOR 裝飾模式

        VisualComponent是一個描述可視對象的抽象類,它定義了繪制和事件處理的接口。注意Decorator類怎樣将繪制請求簡單地發送給它的元件,以及Decorator的子類如何擴充這個操作。

        Decorator的子類為特定功能可以自由地添加一些操作。例如,如果其他對象知道界面中恰好有一個ScrollDecorator對象,這些對象就可以用ScrollDecorator對象的ScrollTo操作滾動這個界面。這個模式中有一點很重要,它使得在VisualComponent可以出現的任何地方都可以有裝飾。是以,客戶通常不會感覺到裝飾過的元件與未裝飾元件之間的差異,也不會與裝飾産生任何依賴關系。

4、适用性

        以下情況使用Decorator模式

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

        · 處理那些可以撤銷的職責。

        · 當不能采用生成子類的方法進行擴充時。一種情況是,可能有大量獨立的擴充,為支援每一種組合将産生大量的子類,使得子類數目呈爆炸性增長。另一種情況可能是因為類定義被隐藏,或類定義不能用于生成子類。

5、結構

DECORATOR 裝飾模式

6、參與者

        · Component(VisualComponent)

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

        · ConcreteComponent(TextView)

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

        · Decorator

          -- 維持一個指向Component對象的指針,并定義一個與Component接口一緻的接口。

        · ConcreteDecorator(BorderDecorator,ScrollDecorator)

          --向元件添加職責。

7、協作

        Decorator将請求轉發給它的Component對象,并有可能在轉發請求前後執行一些附加的動作。

8、效果

        Decorator模式至少有兩個主要優點和兩個缺點:

        1)比靜态繼承更靈活    與對象的靜态繼承(多重繼承)相比,Decorator模式提供了更加靈活的向對象添加職責的方式。可以用添加和分離的方法,用裝飾在運作時刻增加和删除職責。相比之下,繼承機制要求為每個添加的職責建立一個新的子類(例如,BorderScrollableTextView,BorderedTextView)。這會産生許多新的類,并且會增加系統的複雜度。此外,為一個特定的Component類提供多個不同的Decorator類,這就使得你可以對一些職責進行混合和比對。

          使用Decorator模式可以很容易地重複添加一個特性,例如在TextView上添加雙邊框時,僅需将添加兩個BorderDecorator即可。而兩次繼承Border類則極容易出錯。

        2)避免在層次結構高層的類有太多的特征    Decorator模式提供了一種“即用即付”的方法來添加職責。它并不師徒在一個複雜的可定制的類中支援所有可預見的特征,相反,你可以定義一個簡單的類,并且用Decorator類給它逐漸地添加功能。可以從簡單的部件組合出複雜的功能。這樣,應用程式不必為不需要的特征付出代價。同時也更易于不依賴于Decorator所擴充(甚至是不可預知的擴充)的類而獨立地定義新類型的Decorator。擴充一個複雜類的時候,很可能會暴露與添加的職責無關的細節。

        3)Decorator與它的Component不一樣    Decorator是一個透明的包裝。如果我們從對象辨別的觀點出發,一個被裝飾了的元件與這個元件是有差别的,是以,使用裝飾時不應該依賴對象辨別。

        4)有許多小對象    采用Decorator模式進行系統設計往往會産生許多看上去很相似的小對象,這些對象僅僅在他們互相連接配接的方式上有所不同,而不是他們的類或是他們的屬性值有所不同。盡管對于那些了解這些系統的人來說,很容易對他們進行定制,但是很難學習這些系統,排錯也很困難。

9、實作

        使用Decorator模式時應注意以下幾點:

        1)接口的一緻性    裝飾對象的接口必須與它所裝飾的Component的接口是一緻的,是以,所有的ConcreteDecorator類必須有一個公共的父類(至少在C++中如此)。

        2)省略抽象的Decorator類    當你僅需要添加一個職責時,沒有必要定義抽象Decorator類。你常常需要處理現存的類層次結構而不是設計一個新系統,這時你可以把Decorator向Component轉發請求的職責合并到ConcreteDecorator中。

        3)保持Component類的簡單性    為了保證接口的一緻性,元件和裝飾必須有一個公共的Component父類。是以保持這個類的簡單性是很重要的;即,它應集中于定義接口而不是存儲資料。對資料表示的定義應延遲到子類中,否則Component類會變得過于複雜和龐大,因而難以大量使用。賦予Component太多的功能也使得具體的子類有一些他們并不需要的功能的可能性大大增加。

        4)改變對象外殼與改變對象核心    我們可以将Decorator看做一個對象的外殼,它可以改變這個對象的行為。另外一種方法是改變對象的核心。例如,Strategy模式就是一個用于改變核心的很好的模式。

          當Component類原本就很龐大時,使用Decorator模式代價太高,Strategy模式相對更好一些。在Strategy模式中,元件将它的一些行為轉發給一個獨立的政策對象,我們可以替換strategy對象,進而改變或擴充元件的功能。

          例如我們可以将元件繪制邊界的功能延遲到一個獨立的Border對象中,這樣就可以支援不同的邊界風格。這個Border對象是一個Strategy對象,它封裝了邊界繪制政策。我們可以将政策的數目從一個擴充為任意多個,這樣産生的效果與對裝飾進行遞歸嵌套是一樣的。

          由于Decorator模式僅從外部改變元件,是以元件無需對它的裝飾有所了解;也就是說,這些裝飾對該元件是透明的,如下圖所示:

DECORATOR 裝飾模式

          在Strategy模式中,component元件本身知道可能進行那些擴充,是以它必須引用并維護相應的政策,如下圖所示:

DECORATOR 裝飾模式

          基于Strategy的方法可能需要修改component元件以适應新的擴充。另一方面,一個政策可以有自己特定的借口,而裝飾的接口則必須與元件的接口一緻。例如,一個繪制邊框的政策僅需要定義生成邊框的接口(DrawBorder,GetWidth等),這意味着記事Component類很龐大時,政策也可以很小。

10、代碼示例

        網絡上代碼示例很多,可随意參考。

繼續閱讀