装饰者模式(为对象提供加强的接口)
一、裝飾者模式
裝飾者模式:允許你通過(guò)將對(duì)象放入包含行為的特殊封裝對(duì)象中來(lái)為原來(lái)的對(duì)象綁定新的行為,相比生成子類更加靈活。是一種在運(yùn)行期動(dòng)態(tài)給某個(gè)對(duì)象的實(shí)例增加功能的方法。如:在IO的Filter模式就使用了裝飾器模式。在Java標(biāo)準(zhǔn)庫(kù)中,InputStream是抽象類,FileInputStream、ServletInputStream、Socket.getInputStream()這些InputStream都是最終數(shù)據(jù)源。
二、例子
現(xiàn)在,如果要給不同的最終數(shù)據(jù)源增加緩沖功能、計(jì)算簽名功能、加密解密功能,那么,3個(gè)最終數(shù)據(jù)源、3種功能一共需要9個(gè)子類。如果繼續(xù)增加最終數(shù)據(jù)源,或者增加新功能,子類會(huì)爆炸式增長(zhǎng),這種設(shè)計(jì)方式顯然是不可取的。Decorator模式的目的就是把一個(gè)一個(gè)的附加功能,用Decorator的方式給一層一層地累加到原始數(shù)據(jù)源上,最終,通過(guò)組合獲得我們想要的功能。
例如:給FileInputStream增加緩沖和解壓縮功能,用Decorator模式寫出來(lái)如下:
// 創(chuàng)建原始的數(shù)據(jù)源: InputStream fis = new FileInputStream("test.gz"); // 增加緩沖功能: InputStream bis = new BufferedInputStream(fis); // 增加解壓縮功能: InputStream gis = new GZIPInputStream(bis);或者一次性寫成這樣:
InputStream input = new GZIPInputStream( // 第二層裝飾new BufferedInputStream( // 第一層裝飾new FileInputStream("test.gz") // 核心功能));觀察BufferedInputStream和GZIPInputStream,它們實(shí)際上都是從FilterInputStream繼承的,這個(gè)FilterInputStream就是一個(gè)抽象的Decorator。我們用圖把Decorator模式畫出來(lái)如下:
最頂層的Component是接口,對(duì)應(yīng)到IO的就是InputStream這個(gè)抽象類。ComponentA、ComponentB是實(shí)際的子類,對(duì)應(yīng)到IO的就是FileInputStream、ServletInputStream這些數(shù)據(jù)源。Decorator是用于實(shí)現(xiàn)各個(gè)附加功能的抽象裝飾器,對(duì)應(yīng)到IO的就是FilterInputStream。而從Decorator派生的就是一個(gè)一個(gè)的裝飾器,它們每個(gè)都有獨(dú)立的功能,對(duì)應(yīng)到IO的就是BufferedInputStream、GZIPInputStream等。
三、Decorator模式有什么好處
它實(shí)際上把核心功能和附加功能給分開了。核心功能指FileInputStream這些真正讀數(shù)據(jù)的源頭,附加功能指加緩沖、壓縮、解密這些功能。如果我們要新增核心功能,就增加Component的子類,例如ByteInputStream。如果我們要增加附加功能,就增加Decorator的子類,例如CipherInputStream。兩部分都可以獨(dú)立地?cái)U(kuò)展,而具體如何附加功能,由調(diào)用方自由組合,從而極大地增強(qiáng)了靈活性。
如果我們要自己設(shè)計(jì)完整的Decorator模式,應(yīng)該如何設(shè)計(jì)?
我們還是舉個(gè)栗子:假設(shè)我們需要渲染一個(gè)HTML的文本,但是文本還可以附加一些效果,比如加粗、變斜體、加下劃線等。為了實(shí)現(xiàn)動(dòng)態(tài)附加效果,可以采用Decorator模式。
首先,仍然需要定義頂層接口TextNode:
public interface TextNode {// 設(shè)置text:void setText(String text);// 獲取text:String getText(); }對(duì)于核心節(jié)點(diǎn),例如,它需要從TextNode直接繼承:
public class SpanNode implements TextNode {private String text;public void setText(String text) {this.text = text;}public String getText() {return "<span>" + text + "</span>";} }緊接著,為了實(shí)現(xiàn)Decorator模式,需要有一個(gè)抽象的Decorator類:
public abstract class NodeDecorator implements TextNode {protected final TextNode target;protected NodeDecorator(TextNode target) {this.target = target;}public void setText(String text) {this.target.setText(text);} }這個(gè)NodeDecorator類的核心是持有一個(gè)TextNode,即將要把功能附加到的TextNode實(shí)例。接下來(lái)就可以寫一個(gè)加粗功能:
public class BoldDecorator extends NodeDecorator {public BoldDecorator(TextNode target) {super(target);}public String getText() {return "<b>" + target.getText() + "</b>";} }類似的,可以繼續(xù)加ItalicDecorator、UnderlineDecorator等。客戶端可以自由組合這些Decorator:
TextNode n1 = new SpanNode(); TextNode n2 = new BoldDecorator(new UnderlineDecorator(new SpanNode())); TextNode n3 = new ItalicDecorator(new BoldDecorator(new SpanNode())); n1.setText("Hello"); n2.setText("Decorated"); n3.setText("World");System.out.println(n1.getText()); // 輸出<span>Hello</span>System.out.println(n2.getText()); // 輸出<b><u><span>Decorated</span></u></b>System.out.println(n3.getText()); // 輸出<i><b><span>World</span></b></i>四、裝飾者模式結(jié)構(gòu)
五、裝飾模式適合應(yīng)用場(chǎng)景
如果你希望在無(wú)需修改代碼的情況下即可使用對(duì)象, 且希望在運(yùn)行時(shí)為對(duì)象新增額外的行為, 可以使用裝飾模式。
裝飾能將業(yè)務(wù)邏輯組織為層次結(jié)構(gòu), 你可為各層創(chuàng)建一個(gè)裝飾, 在運(yùn)行時(shí)將各種不同邏輯組合成對(duì)象。 由于這些對(duì)象都遵循通用接口,客戶端代碼能以相同的方式使用這些對(duì)象。
如果用繼承來(lái)擴(kuò)展對(duì)象行為的方案難以實(shí)現(xiàn)或者根本不可行, 你可以使用該模式。
許多編程語(yǔ)言使用 final最終關(guān)鍵字來(lái)限制對(duì)某個(gè)類的進(jìn)一步擴(kuò)展。 復(fù)用最終類已有行為的唯一方法是使用裝飾模式: 用封裝器對(duì)其進(jìn)行封裝。
- 類比真實(shí)世界
六、 與其他模式的關(guān)系
適配器模式可以對(duì)已有對(duì)象的接口進(jìn)行修改, 裝飾模式則能在不改變對(duì)象接口的前提下強(qiáng)化對(duì)象功能。 此外, 裝飾還支持遞歸組合, 適配器則無(wú)法實(shí)現(xiàn)。
適配器能為被封裝對(duì)象提供不同的接口, 代理模式能為對(duì)象提供相同的接口, 裝飾則能為對(duì)象提供加強(qiáng)的接口。
責(zé)任鏈模式和裝飾模式的類結(jié)構(gòu)非常相似。 兩者都依賴遞歸組合將需要執(zhí)行的操作傳遞給一系列對(duì)象。 但是, 兩者有幾點(diǎn)重要的不同之處。
- 責(zé)任鏈的管理者可以相互獨(dú)立地執(zhí)行一切操作, 還可以隨時(shí)停止傳遞請(qǐng)求。
- 另一方面, 各種裝飾可以在遵循基本接口的情況下擴(kuò)展對(duì)象的行為。 此外,裝飾無(wú)法中斷請(qǐng)求的傳遞。
組合模式和裝飾的結(jié)構(gòu)圖很相似, 因?yàn)閮烧叨家蕾囘f歸組合來(lái)組織無(wú)限數(shù)量的對(duì)象。裝飾類似于組合, 但其只有一個(gè)子組件。 此外還有一個(gè)明顯不同: 裝飾為被封裝對(duì)象添加了額外的職責(zé), 組合僅對(duì)其子節(jié)點(diǎn)的結(jié)果進(jìn)行了 “求和”。但是, 模式也可以相互合作: 你可以使用裝飾來(lái)擴(kuò)展組合樹中特定對(duì)象的行為。
使用組合和裝飾的設(shè)計(jì)通常可從對(duì)于原型模式的使用中獲益。 你可以通過(guò)該模式來(lái)復(fù)制復(fù)雜結(jié)構(gòu), 而非從零開始重新構(gòu)造。
裝飾可讓你更改對(duì)象的外表, 策略模式則讓你能夠改變其本質(zhì)。
裝飾和代理有著相似的結(jié)構(gòu), 但是其意圖卻非常不同。 這兩個(gè)模式的構(gòu)建都基于組合原則, 也就是說(shuō)一個(gè)對(duì)象應(yīng)該將部分工作委派給另一個(gè)對(duì)象。 兩者之間的不同之處在于代理通常自行管理其服務(wù)對(duì)象的生命周期, 而裝飾的生成則總是由客戶端進(jìn)行控制。
參考文章
參考文章
總結(jié)
以上是生活随笔為你收集整理的装饰者模式(为对象提供加强的接口)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 2020年视频号发展白皮书
- 下一篇: 公司裁员优先裁掉这类员工