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