设计模式 | 装饰模式
1 | 裝飾模式的概述
我們?cè)诹私庋b飾模式之前,先回顧下生活中的幾個(gè)常見(jiàn)現(xiàn)象,舉例如下:
- 新房的裝修,房屋裝修并沒(méi)有改變房屋居住的本質(zhì),但可以讓房屋變得更漂亮,更溫馨,更實(shí)用,更滿(mǎn)足居家需求。
- 相片的包裝,照相館中把原相片清洗出來(lái)后,會(huì)對(duì)上面做些包裝/裝飾,相片鍍膜,添加相框等處理,讓整體更加美觀(guān),防潮保存更長(zhǎng)的時(shí)間。
在軟件設(shè)計(jì)中,類(lèi)似上面的場(chǎng)景我們也可以把對(duì)象在不改變結(jié)構(gòu)的情況下對(duì)其加工擴(kuò)展修飾,使得對(duì)象具有更加強(qiáng)大的功能,這種技術(shù)在設(shè)計(jì)模式中就叫裝飾模式。裝飾模式可以在不改變一個(gè)對(duì)象本身功能的基礎(chǔ)上給對(duì)象增加額外的新行為。
1.1 裝飾模式的定義
- 裝飾模式:動(dòng)態(tài)地給一個(gè)對(duì)象增加一些額外的職責(zé)。就擴(kuò)展功能面言,裝飾模式提供了—種比使用子類(lèi)更加靈活的替代方案。
- Decorator Pattern:Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
裝飾模式是一種對(duì)象結(jié)構(gòu)型模式,它以對(duì)客戶(hù)透明的方式動(dòng)態(tài)地給一個(gè)對(duì)象附加上更多的責(zé)任,可以在不需要?jiǎng)?chuàng)建更多子類(lèi)的情況下,讓對(duì)象的功能得以擴(kuò)展。
裝飾模式是一種用于替代繼承的技術(shù),它通過(guò)一開(kāi)無(wú)須定義子類(lèi)的方式給對(duì)象動(dòng)態(tài)增加職責(zé),使用對(duì)象之間的關(guān)聯(lián)天系取代類(lèi)之間的繼承關(guān)系。裝飾模式降低了系統(tǒng)的耦合度??梢詣?dòng)態(tài)增加或刪除對(duì)象的職責(zé),并使得需要裝飾的具體構(gòu)件類(lèi)和用于裝飾的具體裝飾類(lèi)都可以獨(dú)立變化,增加新的具體構(gòu)件類(lèi)和具體裝飾類(lèi)都非常方便,符合開(kāi)閉原則。
2 |?裝飾模式的結(jié)構(gòu)與實(shí)現(xiàn)
2.1?裝飾模式的結(jié)構(gòu)
類(lèi)圖結(jié)構(gòu)
裝飾模式包含以下 4 個(gè)角色
- (1) Componcnt(抽象構(gòu)件),它是具體構(gòu)件和抽象裝飾類(lèi)的共同父奧,聲明了在具體構(gòu)件中實(shí)轟的業(yè)備方法,它的引入可以使客戶(hù)湖以一致的方式處理未被裝飾的對(duì)象以及裝飾之后的對(duì)象,字現(xiàn)客戶(hù)端的透明操作。
- (2) ConcreteComponent(具體鉤件),它是抽象構(gòu)件類(lèi)的子類(lèi),用于定義具體的構(gòu)件對(duì)象,實(shí)現(xiàn)了在抽象物件中聲明的方法,裝飾類(lèi)可以給它增加額外的職責(zé)(方法)。
- (3) Decorater(抽象裝飾類(lèi)),它也是抽象構(gòu)件類(lèi)的子類(lèi),用于給具體構(gòu)件增加職責(zé),但是具體職責(zé)通常在其子類(lèi)中實(shí)艦,它維護(hù)一個(gè)指向抽象構(gòu)件對(duì)象的引用,通過(guò)該引用可以調(diào)用裝飾之前構(gòu)件對(duì)象的方法,并通過(guò)其子面擴(kuò)展該方法,以達(dá)到裝飾的目的。
- (4) ConcreteDecorator(具體裝飾類(lèi)),它是抽象裝飾類(lèi)的子類(lèi),負(fù)責(zé)向構(gòu)件添加新的職責(zé),每一個(gè)具體裝飾類(lèi)都定義了一些新的行為,它可以調(diào)用在抽象裝飾類(lèi)中定義的方法,并且可以增加新的方法,以實(shí)現(xiàn)擴(kuò)展對(duì)象的行為。
2.2?裝飾模式的實(shí)現(xiàn)
【抽象構(gòu)件類(lèi)/Component】一般設(shè)計(jì)為抽象類(lèi)或者接口,在其中聲明了抽象業(yè)務(wù)方法,當(dāng)然,也可以在抽象構(gòu)件類(lèi)中實(shí)現(xiàn)一些所有具體構(gòu)件類(lèi)都共有的業(yè)務(wù)方法。抽象構(gòu)件類(lèi)的典型代碼如下:
abstract class Component {public abstract void Operation(); }【具體構(gòu)件類(lèi)/ConcreteComponent?】作為抽象構(gòu)件類(lèi)的子類(lèi)實(shí)現(xiàn)了在抽象構(gòu)件類(lèi)中聲明的業(yè)務(wù)方法,通常在具體構(gòu)件類(lèi)中只提供基本功能的實(shí)現(xiàn),一些復(fù)雜的功能需通過(guò)裝飾類(lèi)來(lái)進(jìn)行擴(kuò)展,其典型代碼如下:
class ConcreteComponent : Component {public override void Operation(){Console.WriteLine("基本功能實(shí)現(xiàn)");} }【抽象裝飾類(lèi)/Decorator?】裝飾模式的核心在于裝飾類(lèi)的設(shè)計(jì),其典型代碼如下:
class Decorator : Component {private readonly Component _Component; //維持一個(gè)對(duì)抽象構(gòu)件對(duì)象的引用//注入一個(gè)抽象構(gòu)件類(lèi)型的對(duì)象public Decorator(Component component){_Component = component;}public override void Operation(){_Component.Operation(); //調(diào)用原有業(yè)務(wù)方法} }【具體裝飾類(lèi)/ConcreteDecorator】是抽象裝飾類(lèi)的子類(lèi),可以根據(jù)需要對(duì)父類(lèi)方法?Operation()?進(jìn)行擴(kuò)展,典型代碼如下:
class ConcreteDecorator : Decorator {public ConcreteDecorator(Component component) : base(component){}public override void Operation() {base.Operation(); //調(diào)用原有業(yè)務(wù)方法AddedBehavior(); //調(diào)用新增業(yè)務(wù)方法}//新增業(yè)務(wù)方法public void AddedBehavior() { Console.WriteLine("功能擴(kuò)展實(shí)現(xiàn)");} }在具體裝飾類(lèi)中可以調(diào)用抽象裝飾類(lèi)的?Operation()?方法,同時(shí)可以定義新的業(yè)務(wù)方法,例如?AddedBehavior() ,如果該方法不希望客戶(hù)端單獨(dú)調(diào)用,還可以將其可訪(fǎng)問(wèn)性修改為私有(private)。
客戶(hù)端調(diào)用
3 |?裝飾模式的應(yīng)用實(shí)例
3.1?實(shí)例說(shuō)明
某軟件公司基于面向?qū)ο蠹夹g(shù)開(kāi)發(fā)了一套圖形界面構(gòu)件庫(kù)——VisualComponent。該構(gòu)件庫(kù)提供了大量的基本構(gòu)件,如窗體、文本框、列表框等,由于在使用該構(gòu)件庫(kù)時(shí),用戶(hù)經(jīng)常要求定制一些特殊的顯示效果,如帶滾動(dòng)條的窗體、帶黑色邊框的文本框,既帶滾動(dòng)條又帶黑色邊框的列表框等,因此經(jīng)常需要對(duì)該構(gòu)件庫(kù)進(jìn)行擴(kuò)展以增強(qiáng)其功能,現(xiàn)使用裝飾模式來(lái)設(shè)計(jì)該圖形界面構(gòu)件庫(kù)。
3.2?實(shí)例代碼設(shè)計(jì)
類(lèi)圖結(jié)構(gòu)
示例代碼說(shuō)明
- VisualComponent :抽象界面構(gòu)件類(lèi),充當(dāng)抽象構(gòu)建類(lèi)。
- Window、TextBox、ListBox :VisualComponent?的派生類(lèi),充當(dāng)具體構(gòu)建類(lèi)。
- ComponentDecorator :VisualComponent?的派生類(lèi),充當(dāng)抽象裝飾器類(lèi)。
- BlackBorderDecorator、ScrollBarDecorator :ComponentDecorator?的派生類(lèi),充當(dāng)具體裝飾類(lèi)。
客戶(hù)端調(diào)用
如果需要在原系統(tǒng)中增加一個(gè)新的具體構(gòu)建類(lèi)或者新的具體裝飾類(lèi),無(wú)須修改現(xiàn)有的類(lèi)庫(kù)代碼,只需將他們分別作為抽象構(gòu)建類(lèi)或者抽象裝飾類(lèi)的子類(lèi)即可。
完整代碼示例請(qǐng)查看 =》https://gitee.com/dolayout/DesignPatternOfCSharp/tree/master/DesignPatternOfCSharp/DecoratorPattern
4 |?透明裝飾模式與半透明裝飾模式
在裝飾模式中,具體裝飾類(lèi)通過(guò)新增成員變量或者成員方法來(lái)擴(kuò)展具體構(gòu)建類(lèi)的功能。
- 在標(biāo)準(zhǔn)的裝飾模式中,新增行為需要在原有的業(yè)務(wù)方法中調(diào)用,無(wú)論是具體構(gòu)建對(duì)象還是裝飾過(guò)后的構(gòu)建對(duì)象,對(duì)于客戶(hù)端而言都是透明的,這種裝飾模式被稱(chēng)為透明(Transparent)裝飾模式。
- 但是在某些情況下,有些新增行為可能需要單獨(dú)被調(diào)用,此時(shí),客戶(hù)端不能再一致性地處理裝飾之前的對(duì)象和裝飾之后的對(duì)象,這種裝飾模式被稱(chēng)為半透明(Semi-transparent)裝飾模式。
下面將對(duì)這兩種裝飾模式進(jìn)行較為詳細(xì)的介紹。
4.1 透明裝飾模式
在透明裝飾模式中,要求客戶(hù)端完全針對(duì)抽象編程,裝飾模式的透明性要求客戶(hù)端程序不應(yīng)該將對(duì)象聲明為具體構(gòu)件類(lèi)型或具體裝飾類(lèi)型,而應(yīng)該全部聲明為抽象構(gòu)件類(lèi)型。對(duì)于客戶(hù)端而言,具體構(gòu)件對(duì)象和具體裝飾對(duì)象沒(méi)有任何區(qū)別。即應(yīng)該使用以下代碼:
Component component = new ConcreteComponent(); //推薦:使用抽象構(gòu)件類(lèi)型定義對(duì)象 //ConcreteComponent component = new ConcreteComponent(); //不推薦:使用具體構(gòu)件類(lèi)型定義對(duì)象Decorator decorator = new ConcreteDecorator(component); //推薦:抽象裝飾器類(lèi)型定義對(duì)象 //ConcreteDecorator decorator = new ConcreteDecorator(component); //不推薦:具體裝飾器類(lèi)型定義對(duì)象對(duì)于多次裝飾而言,在客戶(hù)端中存在以下代碼片段:
Component component_o, component_d1, component_d2; //全部使用抽象構(gòu)件定義 component_o = new ConcreteComponent(); component_d1 = new ConcreteDecoratoz1(component_o); component_d2 = new ConcreteDecorator2(component_d1); component_d2.Operation(); //無(wú)法單獨(dú)調(diào)用 component_d2 的 AddedBehavior() 方法使用抽象構(gòu)件類(lèi)型 Component 定義全部具體構(gòu)件對(duì)象和具體裝飾對(duì)象,客戶(hù)端可以一致地使用這此對(duì)象,因此符合透明裝飾模式的要求。
透明裝飾模式可以讓客戶(hù)端透明地使用裝飾之前的對(duì)象和裝飾之后的對(duì)象,無(wú)須關(guān)心它們的區(qū)別,此外,還可以對(duì)一個(gè)已裝飾過(guò)的對(duì)象進(jìn)行多次裝飾,得到更為復(fù)雜、功能更為強(qiáng)大的對(duì)象,在實(shí)現(xiàn)透明裝飾模式時(shí),要求具體裝飾類(lèi)的 Operation() 方法覆蓋抽象裝飾類(lèi)的 Operation() 方法,除了調(diào)用原有對(duì)象的 Operation() 外還需要調(diào)用新增的 AddedBehavior() 方法來(lái)增加新行為。但是由于在抽象構(gòu)件中并沒(méi)有聲明 AddedBehavior() 方法,因此,無(wú)法在客戶(hù)端單獨(dú)調(diào)用該方法,上面的圖形界面構(gòu)件庫(kù)的設(shè)計(jì)方案中使用的就是透明裝飾模式。
4.2?半透明裝飾模式
透明裝飾模式的設(shè)計(jì)難度較大,而且有時(shí)需要單獨(dú)調(diào)用新增的業(yè)務(wù)方法,為了能夠調(diào)用到新增的方法,不得不用具體的裝飾類(lèi)來(lái)定義裝飾后的對(duì)象,而具體構(gòu)件可以繼續(xù)使用抽象對(duì)象構(gòu)件類(lèi)型來(lái)定義,這種裝飾模式即為半透明裝飾模式。
客戶(hù)端調(diào)用
客戶(hù)端示例代碼片段如下:
Component component = new ConcreteComponent(); //使用抽象構(gòu)建類(lèi)型定義 ConcreteDecorator decorator = new ConcreteDecorator(component); //使用具體裝飾類(lèi)型定義 decorator.Operation(); //調(diào)用單獨(dú)的新增業(yè)務(wù)方法半透明裝飾模式可以給系統(tǒng)帶來(lái)更多的靈活性,設(shè)計(jì)相對(duì)簡(jiǎn)單,使用起來(lái)也非常方便;但是其最大的缺點(diǎn)在于不能實(shí)現(xiàn)對(duì)同一個(gè)對(duì)象的多次裝飾,而且客戶(hù)端需要有區(qū)別地對(duì)待裝飾之前的對(duì)象和裝飾之后的對(duì)象。在實(shí)現(xiàn)半透明的裝飾模式時(shí),只需在具體裝飾類(lèi)中增加一個(gè)獨(dú)立的 AddedBehavior() 方法來(lái)封裝相應(yīng)的業(yè)務(wù)處理即可,由于客戶(hù)端使用具體裝飾類(lèi)型來(lái)定義裝飾后的對(duì)象,因此可以單獨(dú)調(diào)用 AddedBehavior() 方法。
5 | 裝飾模式的優(yōu)缺點(diǎn)與適用環(huán)境
裝飾模式降低了系統(tǒng)的耦合度,可以動(dòng)態(tài)增加或刪除對(duì)象的職責(zé),并使得需要裝飾的具體構(gòu)件類(lèi)和用于裝飾的具體裝飾類(lèi)可以獨(dú)立變化,以便增加新的且休構(gòu)件米和且體裝飾類(lèi)。使用裝飾模式將大大減少子類(lèi)的個(gè)數(shù),讓系統(tǒng)擴(kuò)展起來(lái)更加方俑,而日更災(zāi)具維護(hù),是取代繼承復(fù)用的有效方式之一。在軟件開(kāi)發(fā)中,裝飾模式得到了較為廣泛的應(yīng)用。
5.1 裝飾模式的優(yōu)點(diǎn)
- (1)對(duì)于擴(kuò)展一個(gè)對(duì)象的功能,裝飾模式比繼承更加靈活,不會(huì)導(dǎo)致類(lèi)的個(gè)數(shù)急劇增加。
- (2)裝飾模式可以通過(guò)一種動(dòng)態(tài)的方式來(lái)擴(kuò)展一個(gè)對(duì)象的功能,通過(guò)配置文件可以在運(yùn)行時(shí)選擇不同的具體裝飾類(lèi),從而實(shí)現(xiàn)不同的行為。
- (3)裝飾模式可以對(duì)一個(gè)對(duì)象進(jìn)行多次裝飾,通過(guò)使用不同的具體裝飾類(lèi)以及這些裝飾類(lèi)的排列組合,可以創(chuàng)造出很多不同行為的組合,得到功能更為強(qiáng)大的對(duì)象。
- (4)在裝飾模式中,具體構(gòu)件類(lèi)與具體裝飾類(lèi)是可以獨(dú)立變化的,用戶(hù)可以根據(jù)需要增加新的具體構(gòu)件類(lèi)和具體裝飾類(lèi),且原有類(lèi)庫(kù)代碼無(wú)須改變,符合開(kāi)閉原則。
5.2 裝飾模式的缺點(diǎn)
- (1)使用裝飾模式進(jìn)行系統(tǒng)設(shè)計(jì)時(shí)將產(chǎn)生很多小對(duì)象,這些對(duì)象的區(qū)別在于它們之間相互連接的方式有所不同,而不是它們的類(lèi)或者屬性值有所不同,大量小對(duì)象的產(chǎn)生勢(shì)必會(huì)占用更多的系統(tǒng)資源,在一定程度上影響程序的性能。
- (2)裝飾模式提供了一種比繼承更加靈活機(jī)動(dòng)的解決方案,但同時(shí)也意味著比繼承更加易于出錯(cuò),排錯(cuò)也更困難,對(duì)于多次裝飾的對(duì)象,調(diào)試時(shí)尋找錯(cuò)誤可能需要逐級(jí)排查,較為煩瑣。
5.3 裝飾模式的適用環(huán)境
- (1)在不影響其他對(duì)象的情況下,以動(dòng)態(tài)、透明的方式給單個(gè)對(duì)象添加職責(zé)。
- (2)當(dāng)不能采用繼承的方式對(duì)系統(tǒng)進(jìn)行擴(kuò)展或者采用繼承不利于系統(tǒng)擴(kuò)展和維護(hù)時(shí)可以使用裝飾模式。
不能采用繼承的情況主要有兩種:
總結(jié)
以上是生活随笔為你收集整理的设计模式 | 装饰模式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 日志@Slf4j介绍使用及配置等级
- 下一篇: asp.net ajax控件工具集 Au