裝飾者Decorator
背景
問題
- 你如何組織你的代碼使其可以容易的添加基本的或者一些很少用到的 特性,而不是直接不額外的代碼寫在你的類的內(nèi)部?
解決方案
- 裝飾器模式: 動態(tài)地給一個(gè)對象添加一些額外的職責(zé)或者行為。就增加功能來說, Decorator模式相比生成子類更為靈活。
- 裝飾器模式提供了改變子類的靈活方案。裝飾器模式在不必改變原類文件和使用繼承的情況下,動態(tài)的擴(kuò)展一個(gè)對象的功能。它是通過創(chuàng)建一個(gè)包裝對象,也就是裝飾來包裹真實(shí)的對象。
- 當(dāng)用于一組子類時(shí),裝飾器模式更加有用。如果你擁有一族子類(從一個(gè)父類派生而來),你需要在與子類獨(dú)立使用情況下添加額外的特性,你可以使用裝飾器模式,以避免代碼重復(fù)和具體子類數(shù)量的增加。
剖析
意圖
- 將額外的責(zé)任自動附加到一個(gè)對象上。裝飾模式提供了靈活的子類來擴(kuò)展功能
- client-specified 通過封裝裝飾了一個(gè)核心對象
- 包裝禮物,放進(jìn)盒子里,包裝盒子
定義
- 動態(tài)地改一個(gè)對象添加一些額外的職責(zé)。就增加功能來說,裝飾模式比生成子類更為靈活
本質(zhì)
- 動態(tài)組合
模式講解
- 動態(tài)的實(shí)現(xiàn)為對象添加功能
- 思考起點(diǎn): 對象組合
- 裝飾器:給被裝飾對象增加功能,還可以根據(jù)需要選擇是否調(diào)用裝飾對象的功能
- 裝飾器一定要和組件類一致的接口,保證他們是同一個(gè)類型,并具有同一個(gè)外觀
- 退化形式:沒有必要設(shè)計(jì)抽象類了
特點(diǎn)
(1) 裝飾對象和真實(shí)對象有相同的接口。這樣客戶端對象就可以以和真實(shí)對象相同的方式和裝飾對象交互。
(2) 裝飾對象包含一個(gè)真實(shí)對象的索引(reference)
(3) 裝飾對象接受所有的來自客戶端的請求。它把這些請求轉(zhuǎn)發(fā)給真實(shí)的對象。
(4) 裝飾對象可以在轉(zhuǎn)發(fā)這些請求以前或以后增加一些附加功能。這樣就確保了在運(yùn)行時(shí),不用修改給定對象的結(jié)構(gòu)就可以在外部增加附加的功能。在面向?qū)ο蟮脑O(shè)計(jì)中,通常是通過繼承來實(shí)現(xiàn)對給定類的功能擴(kuò)展。
UML
模式的組成
抽象組件角色(Component):定義一個(gè)對象接口,以規(guī)范準(zhǔn)備接受附加責(zé)任的對象,
即可以給這些對象動態(tài)地添加職責(zé)。
具體組件角色(ConcreteComponent) :被裝飾者,定義一個(gè)將要被裝飾增加功能的類。
可以給這個(gè)類的對象添加一些職責(zé)
抽象裝飾器(Decorator):維持一個(gè)指向構(gòu)件Component對象的實(shí)例,
并定義一個(gè)與抽象組件角色Component接口一致的接口
具體裝飾器角色(ConcreteDecorator):向組件添加職責(zé)。
Example
場景
- 何時(shí)使用
1.想在不影響其它對象的情況下,以動態(tài),透明的方式給單個(gè)對象添加職責(zé)。
2.想要擴(kuò)展一個(gè)類的行為,卻做不到。類定義可能被隱藏,無法進(jìn)行子類化,或者對類的每個(gè)行為的擴(kuò)展,為支持每種功能組合,將產(chǎn)生大量的子類。
- 使用場景
1.需要擴(kuò)展一個(gè)類的功能,或給一個(gè)類增加附加功能
2.需要動態(tài)的給一個(gè)對象添加功能,這些功能可以再動態(tài)的側(cè)撤銷
3.為一批兄弟類進(jìn)行改裝或加裝功能
對繼承的有力補(bǔ)充
優(yōu)缺點(diǎn)
- 比靜態(tài)繼承更靈活: 與對象的靜態(tài)繼承(多重繼承)相比, Decorator模式提供了更加靈活的向?qū)ο筇砑勇氊?zé)的方式。可以用添加和分離的方法,用裝飾在運(yùn)行時(shí)刻增加和刪除職責(zé)。相比之下,繼承機(jī)制要求為每個(gè)添加的職責(zé)創(chuàng)建一個(gè)新的子類。這會產(chǎn)生許多新的類,并且會增加系統(tǒng)的復(fù)雜度。此外,為一個(gè)特定的Component類提供多個(gè)不同的 Decorator類,這就使得你可以對一些職責(zé)進(jìn)行混合和匹配。使用Decorator模式可以很容易地重復(fù)添加一個(gè)特性。
- 避免在層次結(jié)構(gòu)高層的類有太多的特征 Decorator模式提供了一種“即用即付”的方法來添加職責(zé)。它并不試圖在一個(gè)復(fù)雜的可定制的類中支持所有可預(yù)見的特征,相反,你可以定義一個(gè)簡單的類,并且用 Decorator類給它逐漸地添加功能。可以從簡單的部件組合出復(fù)雜的功能。這樣,應(yīng)用程序不必為不需要的特征付出代價(jià)。同時(shí)更易于不依賴于 Decorator所擴(kuò)展(甚至是不可預(yù)知的擴(kuò)展)的類而獨(dú)立地定義新類型的 Decorator。擴(kuò)展一個(gè)復(fù)雜類的時(shí)候,很可能會暴露與添加的職責(zé)無關(guān)的細(xì)節(jié)。
- Decorator與它的Component不一樣 Decorator是一個(gè)透明的包裝。如果我們從對象標(biāo)識的觀點(diǎn)出發(fā),一個(gè)被裝飾了的組件與這個(gè)組件是有差別的,因此,使用裝飾不應(yīng)該依賴對象標(biāo)識。
- 有許多小對象 采用Decorator模式進(jìn)行系統(tǒng)設(shè)計(jì)往往會產(chǎn)生許多看上去類似的小對象,這些對象僅僅在他們相互連接的方式上有所不同,而不是它們的類或是它們的屬性值有所不同。盡管對于那些了解這些系統(tǒng)的人來說,很容易對它們進(jìn)行定制,但是很難學(xué)習(xí)這些系統(tǒng),排錯也很困難。
優(yōu)點(diǎn)
- 比繼承更靈活
- 更容易復(fù)用功能
- 簡化高層定義
缺點(diǎn)
- 產(chǎn)生很多細(xì)粒度對象
- 相關(guān)模式
- 策略模式
相關(guān)模式
1)Adapter 模式:Decorator模式不同于Adapter模式,因?yàn)檠b飾僅改變對象的職責(zé)而
不改變它的接口;而適配器將給對象一個(gè)全新的接口。
2)Composite模式:可以將裝飾視為一個(gè)退化的、僅有一個(gè)組件的組
合。然而,裝飾僅給對象添加一些額外的職責(zé)—它的目的不在于對象聚集。
3)Strategy模式:用一個(gè)裝飾你可以改變對象的外表;而Strategy模
式使得你可以改變對象的內(nèi)核。這是改變對象的兩種途徑。
經(jīng)驗(yàn)法則
- 適配器提供不同的接口給他的子對象。代理提供相同的接口,裝飾模式增強(qiáng)了接口
- 適配器改變了對象的接口,裝飾器加強(qiáng)了對象的責(zé)任。裝飾器模式對客戶端更加透明。因此,裝飾支持遞歸組合,并不是純粹的適配器
- 組合模式和裝飾器模式有相同結(jié)構(gòu)的語法,反映了遞歸組合管理開放對象樹
- 修飾符可以被視為一種只有一個(gè)退化符合組件。裝飾模式增加了額外的模式--他不是面向?qū)ο缶酆?/li>
- 裝飾模式設(shè)計(jì)目的為了讓你給對象增加責(zé)任而不是子類。組合類的目的不是裝飾而是表示。他們的目的不同,但是互補(bǔ)。
- 組合使用責(zé)任鏈模式通過父類訪問全局性。在組合模式部分組成中也使用裝飾模式覆蓋這些屬性。
- 裝飾模式和代理模式有不同的目的但是相同的結(jié)構(gòu)。提供了另一個(gè)對象間接層。,實(shí)現(xiàn)了保留了一個(gè)引用到前向請求的對象。
- 裝飾模式改變了對象的外部。策略模式改變了內(nèi)部。
總結(jié)
1)使用裝飾器設(shè)計(jì)模式設(shè)計(jì)類的目標(biāo)是: 不必重寫任何已有的功能性代碼,而是對某個(gè)基于對象應(yīng)用增量變化。
- 裝飾器設(shè)計(jì)模式采用這樣的構(gòu)建方式: 在主代碼流中應(yīng)該能夠直接插入一個(gè)或多個(gè)更改或“裝飾”目標(biāo)對象的裝飾器,同時(shí)不影響其他代碼流。
- Decorator模式采用對象組合而非繼承的手法,實(shí)現(xiàn)了在運(yùn)行時(shí)動態(tài)的擴(kuò)展對象功能的能力,而且可以根據(jù)需要擴(kuò)展多個(gè)功能,避免了單獨(dú)使用繼承帶來的“靈活性差”和“多子類衍生問題”。
同時(shí)它很好地符合面向?qū)ο笤O(shè)計(jì)原則中“優(yōu)先使用對象組合而非繼承”和“開放-封閉”原則。
也許裝飾器模式最重要的一個(gè)方面是它的超過繼承的能力。“問題”部分展現(xiàn)了一個(gè)使用繼承的子類爆炸。
基于裝飾器模式的解決方案,UML類圖展現(xiàn)了這個(gè)簡潔靈活的解決方案。
- Decorator模式采用對象組合而非繼承的手法,實(shí)現(xiàn)了在運(yùn)行時(shí)動態(tài)的擴(kuò)展對象功能的能力,而且可以根據(jù)需要擴(kuò)展多個(gè)功能,避免了單獨(dú)使用繼承帶來的“靈活性差”和“多子類衍生問題”。
從外部變更
每個(gè)節(jié)點(diǎn)不知道裝飾(外表的變更)
策略(內(nèi)容的變更)
從內(nèi)部變更
每個(gè)節(jié)點(diǎn)知道一組預(yù)定義的變更方式