代理模式与装饰器模式有何区别?
本文有一些爭(zhēng)議,談?wù)摰氖窃O(shè)計(jì)模式中的代理模式和裝飾器模式的區(qū)別,但筆者是非常贊同文章的觀點(diǎn)的,有種豁然開(kāi)朗的感覺(jué)。
以下是原文
代理模式與裝飾器模式有何區(qū)別?
我想有必要對(duì)此問(wèn)題談一下我的個(gè)人理解,若有誤導(dǎo)的之處,還請(qǐng)大家指正!
它們的區(qū)別就是,Proxy 模式需要的是一個(gè)能人,而 Decorator 模式需要的是一個(gè)團(tuán)隊(duì)。
有些情況下,擁有了一個(gè)團(tuán)隊(duì),會(huì)更加利于工作分工,而不至于將所有的事情,都讓這個(gè)能人來(lái)干,他終將有一天會(huì) hold 不住的。但有些情況下,人多了反而不好,只需要一個(gè)能人就行了。
如果這個(gè)比喻不太恰當(dāng)?shù)脑?#xff0c;我就要拿出我的殺手锏了,用代碼來(lái)說(shuō)話。
我們先來(lái)回憶一下這兩段經(jīng)典的代碼,一個(gè)接口,一個(gè)它的實(shí)現(xiàn)類(lèi)。
public interface Greeting {void sayHello(String name); } public class GreetingImpl implements Greeting {@Overridepublic void sayHello(String name) {System.out.println("Hello! " + name);} }可以使用 Proxy 類(lèi)來(lái)代理 GreetingImpl 類(lèi)做點(diǎn)事情:public class GreetingProxy implements Greeting {private GreetingImpl greetingImpl;public GreetingProxy(GreetingImpl greetingImpl) {this.greetingImpl = greetingImpl;}@Overridepublic void sayHello(String name) {before();greetingImpl.sayHello(name);}private void before() {System.out.println("Before");} }只需保證 GreetingProxy 與 GreetingImpl 實(shí)現(xiàn)同一個(gè)接口 Greeting,并通過(guò)構(gòu)造方法將 GreetingImpl 溫柔地射入 GreetingProxy 的身體之中,那么,GreetingProxy 就可以完全擁有 GreetingImpl 了。可以在幫它做正事兒之前,先干點(diǎn)別的事情,比如這里的 before() 方法。想干點(diǎn)什么就干點(diǎn)什么,只要您喜歡,它就喜歡。(此處省略一千字)
以上就是 Proxy 模式,可以認(rèn)為 GreetingProxy 包裝了 GreetingImpl,那么,我們就應(yīng)該怎樣來(lái)使用呢?
public class ClientProxy {public static void main(String[] args) {Greeting greeting = new GreetingProxy(new GreetingImpl());greeting.sayHello("Jack");} }很爽吧?下面用一張類(lèi)圖來(lái)表達(dá)我此時(shí)此刻的感覺(jué):
可見(jiàn),GreetingProxy 是通過(guò)“組合”的方式對(duì) GreetingImpl 進(jìn)行包裝,并對(duì)其進(jìn)行功能擴(kuò)展。這樣,無(wú)需修改 GreetingImpl 的任何一行代碼,就可以完成它想要做的事情。
說(shuō)的高深一點(diǎn),這就是“開(kāi)閉原則”(可不是一開(kāi)一閉的意思哦),它是設(shè)計(jì)模式中一條非常重要的原則,意思就是“對(duì)擴(kuò)展開(kāi)放,對(duì)修改封閉”。沒(méi)錯(cuò),我們確實(shí)是提供了 GreetingProxy 類(lèi)來(lái)擴(kuò)展 GreetingImpl 的功能,而并非去修改 GreetingImpl 原有的代碼。這就是超牛逼的“開(kāi)閉原則”了,每個(gè)開(kāi)發(fā)人員都需要銘記在心!還需要知道的就是擴(kuò)展并非只有“繼承”這一種方式,這里用到的“組合”也是一種擴(kuò)展技巧。
其實(shí),以上使用 Proxy 模式實(shí)現(xiàn)了 AOP 理論中的 Before Advice(前置增強(qiáng))功能。如果用戶現(xiàn)在來(lái)了一個(gè)需求,需要在 sayHello 完事之后再記錄一點(diǎn)操作日志。那么,我們此時(shí)最簡(jiǎn)單的方法就是給 GreetingProxy 增加一個(gè) after() 方法,代碼如下:
public class GreetingProxy implements Greeting {private GreetingImpl greetingImpl;public GreetingProxy(GreetingImpl greetingImpl) {this.greetingImpl = greetingImpl;}@Overridepublic void sayHello(String name) {before();greetingImpl.sayHello(name);after();}private void before() {System.out.println("Before");}private void after() {System.out.println("After");} }這樣做確實(shí)可以實(shí)現(xiàn)需求,但您要知道,需求是永無(wú)止境的,這個(gè) Proxy 類(lèi)將來(lái)可能會(huì)非常龐大,要干的事情會(huì)越來(lái)越多。一下子是日志記錄,一下子是事務(wù)控制,還有權(quán)限控制,還有數(shù)據(jù)緩存。把所有的功能都放在這個(gè) Proxy 類(lèi)中是不明智的,同時(shí)這也違反了“開(kāi)閉原則”。
作為一個(gè)牛逼的架構(gòu)師,有必要來(lái)點(diǎn)炫的東西,讓那幫程序員小弟們對(duì)您投來(lái)崇拜的目光。
用 Decorator 模式吧!
先來(lái)一張牛圖:
搞了一個(gè)抽象類(lèi) GreetingDecorator 出來(lái),確實(shí)挺抽象的,它就是傳說(shuō)中的“裝飾器”了,也實(shí)現(xiàn)了 Greeting 接口(與 Proxy 模式相同),但卻有兩點(diǎn)不同:
在裝飾器中不是組合實(shí)現(xiàn)類(lèi) GreetingImpl,而是組合它的接口 Greeting。
下面通過(guò)兩個(gè) Decorator 的實(shí)現(xiàn)類(lèi)(也就是具體裝飾器),來(lái)提供多種功能的擴(kuò)展。
我們不再需要一個(gè)能人,而需要一個(gè)團(tuán)隊(duì)!
如果要加入日志記錄功能,可以搞一個(gè)日志記錄的裝飾器;如果要加入事務(wù)控制功能,也可以再搞一個(gè)事務(wù)控制的裝飾器;...
想怎么裝飾就怎么裝飾,這就像您買(mǎi)了一套新房,現(xiàn)在都是毛坯的,您可以刷漆,也可以貼紙,還可以畫(huà)畫(huà),當(dāng)然可以又刷漆、又貼紙、又畫(huà)畫(huà)。
屁話少說(shuō),上代碼吧!
先來(lái)看看這個(gè)裝飾器:
public abstract class GreetingDecorator implements Greeting {private Greeting greeting;public GreetingDecorator(Greeting greeting) {this.greeting = greeting;}@Overridepublic void sayHello(String name) {greeting.sayHello(name);} }
以上是一個(gè)很干凈的裝飾器,沒(méi)有任何的增強(qiáng)邏輯,只是簡(jiǎn)單的通過(guò)構(gòu)造方法射入了 Greeting 對(duì)象,然后調(diào)用它自己的 sayHello() 方法,感覺(jué)啥也沒(méi)干一樣。
當(dāng)然,GreetingDecorator 只是一個(gè)抽象的裝飾器,要想真正使用它,您得去繼承它,實(shí)現(xiàn)具體的裝飾器才行。
第一個(gè)具體裝飾器 GreetingBefore:
public class GreetingBefore extends GreetingDecorator {public GreetingBefore(Greeting greeting) {super(greeting);}@Overridepublic void sayHello(String name) {before();super.sayHello(name);}private void before() {System.out.println("Before");} }
第二個(gè)具體裝飾器 GreetingAfter:
需要注意的是,在具體裝飾器的構(gòu)造方法中調(diào)用了父類(lèi)的構(gòu)造方法,也就是把 Greeting 實(shí)例射進(jìn)去了。在具體裝飾器中,完成自己應(yīng)該完成的事情。真正做到了各施其責(zé),而不是一人包攬。
我們可以這樣來(lái)用裝飾器:
public class ClientDecorator {public static void main(String[] args) {Greeting greeting = new GreetingAfter(new GreetingBefore(new GreetingImpl()));greeting.sayHello("Jack");} }先 new GreetingImpl,再 new GreetingBefore,最后 new GreetingAfter。一層裹一層,就像洋蔥一樣!但不同的是,裹的順序是可以交換,比如,先 new GreetingAfter,再 new GreetingBefore。
這種創(chuàng)建對(duì)象的方式是不是非常眼熟呢?沒(méi)錯(cuò)!在 JDK 的?IO 包中也有類(lèi)似的現(xiàn)象。
比如:想讀取一個(gè)二進(jìn)制文件,可以這樣獲取一個(gè)輸入流:
InputStream input = new DataInputStream(new BufferedInputStream(new FileInputStream("C:/test.exe")));
其實(shí)看看 IO 的類(lèi)圖,就一目了然了,它就用到了 Decorator 模式:
IO 包是一個(gè)很強(qiáng)大的包,為了使表達(dá)更加簡(jiǎn)化,以上僅提供了 IO 中的部分類(lèi)。
到此,想必您已經(jīng)了解到 Proxy 模式與 Decorator 模式的本質(zhì)區(qū)別了吧?
這里兩個(gè)模式都是對(duì)類(lèi)的包裝,在不改變類(lèi)自身的情況下,為其添加特定的功能。若這些功能比較單一,可考慮使用 Proxy 模式,但對(duì)于功能較多且需動(dòng)態(tài)擴(kuò)展的情況下,您不妨嘗試一下 Decorator 模式吧!
后文:爭(zhēng)議的評(píng)論
有部分人認(rèn)為,代理模式和裝飾器模式的本質(zhì)區(qū)別是從作用區(qū)分的:
裝飾者模式是使用的調(diào)用者從外部傳進(jìn)來(lái)的被裝飾對(duì)象,調(diào)用者只想讓你把他給你的對(duì)象加強(qiáng)一下,裝飾一下. 代理模式使用的是代理對(duì)象在自己的構(gòu)造方法里面new的一個(gè)被代理類(lèi)的對(duì)象,不是調(diào)用者傳入的,調(diào)用者不知道你找了其他人,他也不關(guān)心這些事,只要你把事情做好就行了.
根據(jù)《設(shè)計(jì)模式之蟬》的描述,代理模式可淺分為強(qiáng)制代理和普通代理,文中應(yīng)該描述的是強(qiáng)制代理,所以筆者不贊同評(píng)論中關(guān)于使用的說(shuō)法。但作用上的區(qū)分是有的,代理更多在于控制或完成額外的功能,裝飾器更多是對(duì)原本功能的加強(qiáng)和修飾。
總結(jié)
以上是生活随笔為你收集整理的代理模式与装饰器模式有何区别?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【报告分享】2021年移动互联网行业白皮
- 下一篇: NetDevice 配置转以太网模块 Y