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