深入理解Java設(shè)計(jì)模式之裝飾模式
一、前言
裝飾模式實(shí)際上是一直提倡的組合代替繼承的實(shí)踐方式,個人認(rèn)為要理解裝飾者模式首先需要理解為什么需要組合代替繼承,繼承又是為什么讓人深惡痛絕.
為什么建議使用組合代替繼承?
面向?qū)ο蟮奶匦杂欣^承與封裝,但兩者卻又有一點(diǎn)矛盾,繼承意味子類依賴了父類中的實(shí)現(xiàn),一旦父類中改變實(shí)現(xiàn)則會對子類造成影響,這是打破了封裝性的一種表現(xiàn). 而組合就是巧用封裝性來實(shí)現(xiàn)繼承功能的代碼復(fù)用.
二、什么是裝飾模式
1.定義:
裝飾器模式又名包裝(Wrapper)模式。裝飾器模式以對客戶端透明的方式拓展對象的功能,是繼承關(guān)系的一種替代方案。
2.意圖
動態(tài)地給一個對象添加一些額外的職責(zé)。就增加功能來說,Decorator模式相比生成子類更為靈活。
3.別名
包裝器Wrapper
4.動機(jī)
有時我們希望給某個對象而不是整個類添加一些功能。例如,一個圖形用戶界面工具箱允許你對任意一個用戶界面組件添加一些組件,例如邊框,或是一些行為,例如窗口滾動等。
5.作用
在不修改原有的接口的情況下,讓類表現(xiàn)的更好。
6.問題
自然是繼承有一些問題
繼承會導(dǎo)致超類和子類之間存在強(qiáng)耦合性,當(dāng)超類改變時,子類也會隨之改變;
超類的內(nèi)部細(xì)節(jié)對于子類是可見的,繼承常常被認(rèn)為破壞了封裝性;
三、裝飾模式的結(jié)構(gòu)

在裝飾器模式中的角色有:
- 抽象構(gòu)件(
Component)角色:給出一個抽象接口,已規(guī)范準(zhǔn)備接收附加責(zé)任的對象。 - 具體構(gòu)件(
ConcreteComponent)角色:定義一個將要接收附加責(zé)任的類 - 裝飾(
Decorator)角色:持有一個構(gòu)件(Component)對象的實(shí)例,并定義一個與抽象構(gòu)件接口一致的接口。 - 具體裝飾(
ConcreteDecorator)角色:負(fù)責(zé)給構(gòu)件對象“貼上”附加的責(zé)任。
四、裝飾模式的使用場景
1.需要擴(kuò)展一個類的功能或給一個類增加附加責(zé)任。
2.需要動態(tài)地給一個對象增加功能,這些功能可以再動態(tài)地撤銷。
3.需要增加由一些基本功能的排列組合而產(chǎn)生的非常大量的功能
五、裝飾模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
1.裝飾這模式和繼承的目的都是擴(kuò)展對象的功能,但裝飾者模式比繼承更靈活
2.通過使用不同的具體裝飾類以及這些類的排列組合,設(shè)計(jì)師可以創(chuàng)造出很多不同行為的組合
3.裝飾者模式有很好地可擴(kuò)展性
缺點(diǎn):
裝飾者模式會導(dǎo)致設(shè)計(jì)中出現(xiàn)許多小對象,如果過度使用,會讓程序變的更復(fù)雜。并且更多的對象會是的差錯變得困難,特別是這些對象看上去都很像。
六、裝飾模式的實(shí)現(xiàn)
/// <summary>
/// 手機(jī)抽象類,即裝飾者模式中的抽象組件類
/// </summary>
public abstract class Phone
{
public abstract void Print();
}
/// <summary>
/// 蘋果手機(jī),即裝飾著模式中的具體組件類
/// </summary>
public class ApplePhone:Phone
{
/// <summary>
/// 重寫基類方法
/// </summary>
public override void Print()
{
Console.WriteLine("開始執(zhí)行具體的對象——蘋果手機(jī)");
}
}
/// <summary>
/// 裝飾抽象類,要讓裝飾完全取代抽象組件,所以必須繼承自Photo
/// </summary>
public abstract class Decorator:Phone
{
private Phone phone;
public Decorator(Phone p)
{
this.phone = p;
}
public override void Print()
{
if (phone != null)
{
phone.Print();
}
}
}
/// <summary>
/// 貼膜,即具體裝飾者
/// </summary>
public class Sticker : Decorator
{
public Sticker(Phone p)
: base(p)
{
}
public override void Print()
{
base.Print();
// 添加新的行為
AddSticker();
}
/// <summary>
/// 新的行為方法
/// </summary>
public void AddSticker()
{
Console.WriteLine("現(xiàn)在蘋果手機(jī)有貼膜了");
}
}
/// <summary>
/// 手機(jī)掛件
/// </summary>
public class Accessories : Decorator
{
public Accessories(Phone p)
: base(p)
{
}
public override void Print()
{
base.Print();
// 添加新的行為
AddAccessories();
}
/// <summary>
/// 新的行為方法
/// </summary>
public void AddAccessories()
{
Console.WriteLine("現(xiàn)在蘋果手機(jī)有漂亮的掛件了");
}
}
客戶端代碼
class Customer
{
static void Main(string[] args)
{
// 我買了個蘋果手機(jī)
Phone phone = new ApplePhone();
// 現(xiàn)在想貼膜了
Decorator applePhoneWithSticker = new Sticker(phone);
// 擴(kuò)展貼膜行為
applePhoneWithSticker.Print();
Console.WriteLine("----------------------\n");
// 現(xiàn)在我想有掛件了
Decorator applePhoneWithAccessories = new Accessories(phone);
// 擴(kuò)展手機(jī)掛件行為
applePhoneWithAccessories.Print();
Console.WriteLine("----------------------\n");
// 現(xiàn)在我同時有貼膜和手機(jī)掛件了
Sticker sticker = new Sticker(phone);
Accessories applePhoneWithAccessoriesAndSticker = new Accessories(sticker);
applePhoneWithAccessoriesAndSticker.Print();
Console.ReadLine();
}
從上面的客戶端代碼可以看出,客戶端可以動態(tài)地將手機(jī)配件增加到手機(jī)上,如果需要添加手機(jī)外殼時,此時只需要添加一個繼承Decorator的手機(jī)外殼類,從而,裝飾模式擴(kuò)展性也非常好。
七、裝飾模式的.NET應(yīng)用
在.NET 類庫中也有裝飾者模式的實(shí)現(xiàn),該類就是System.IO.Stream
MemoryStream memoryStream = new MemoryStream(new byte[] {95,96,97,98,99});
// 擴(kuò)展緩沖的功能
BufferedStream buffStream = new BufferedStream(memoryStream);
// 添加加密的功能
CryptoStream cryptoStream = new CryptoStream(memoryStream,new AesManaged().CreateEncryptor(),CryptoStreamMode.Write);
// 添加壓縮功能
GZipStream gzipStream = new GZipStream(memoryStream, CompressionMode.Compress, true);
八、總結(jié)
裝飾者模式本質(zhì)上來說是AOP思想的一種實(shí)現(xiàn)方式,其持有被裝飾者,因此可以控制被裝飾者的行為從而達(dá)到了AOP的效果。
要點(diǎn):
1:繼承屬于擴(kuò)展形式一種,但不見的是達(dá)到彈性設(shè)計(jì)的最佳方式,組合優(yōu)于繼承。
2:應(yīng)該允許行為可以被拓展,而無需修改現(xiàn)有的代碼。
3:裝飾者模式意味著一群裝飾者類,這些類用來包裝具體組件。
4:裝飾者類反映出被裝飾組件類型。
5:可以使用無數(shù)個裝飾者包裝一個組件。
6:裝飾者會導(dǎo)致設(shè)計(jì)中出現(xiàn)許多小對象,如果過度使用,會讓程序變得很復(fù)雜。
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
Java實(shí)現(xiàn)的模糊匹配某文件夾下的文件并刪除功能示例
這篇文章主要介紹了Java實(shí)現(xiàn)的模糊匹配某文件夾下的文件并刪除功能,涉及java針對目錄與文件的遍歷、匹配、判斷、刪除等相關(guān)操作技巧,需要的朋友可以參考下2018-02-02
SpringMVC源碼解讀之HandlerMapping - AbstractUrlHandlerMapping系列re
這篇文章主要介紹了SpringMVC源碼解讀之HandlerMapping - AbstractUrlHandlerMapping系列request分發(fā) 的相關(guān)資料,需要的朋友可以參考下2016-02-02
SpringBoot+Vue前后端分離實(shí)現(xiàn)審核功能的示例
在實(shí)際開發(fā)中,審核功能是一個非常常用的功能,本文就來介紹一下使用SpringBoot+Vue前后端分離實(shí)現(xiàn)審核功能的示例,具有一定的參考價值,感興趣的可以了解一下2024-02-02
SpringBoot中間件ORM框架實(shí)現(xiàn)案例詳解(Mybatis)
這篇文章主要介紹了SpringBoot中間件ORM框架實(shí)現(xiàn)案例詳解(Mybatis),本篇文章提煉出mybatis最經(jīng)典、最精簡、最核心的代碼設(shè)計(jì),來實(shí)現(xiàn)一個mini-mybatis,從而熟悉并掌握ORM框架的涉及實(shí)現(xiàn),需要的朋友可以參考下2023-07-07
SpringBoot生成jar/war包的布局應(yīng)用
在 Spring Boot 中,"布局應(yīng)用"(Application Layout)指的是打包生成的可執(zhí)行 jar 或 war 文件中的內(nèi)容組織結(jié)構(gòu),本文給大家介紹了SpringBoot生成jar/war包的布局應(yīng)用,需要的朋友可以參考下2024-02-02
Spark學(xué)習(xí)筆記 (二)Spark2.3 HA集群的分布式安裝圖文詳解
這篇文章主要介紹了Spark2.3 HA集群的分布式安裝,結(jié)合圖文與實(shí)例形式詳細(xì)分析了Spark2.3 HA集群分布式安裝具體下載、安裝、配置、啟動及執(zhí)行spark程序等相關(guān)操作技巧,需要的朋友可以參考下2020-02-02

