最近看了馬士兵老師的設(shè)計(jì)模式視頻,感覺其中最難也最感興趣的就是代理模式了。馬士兵老師從靜態(tài)代理的兩種基本方式出發(fā),到初步實(shí)現(xiàn)指定接口的動(dòng)態(tài)代理,最后到模仿 JDK 的動(dòng)態(tài)代理,講的都很詳細(xì)。現(xiàn)在我來給大家梳理一下整個(gè)思路,希望對(duì)大家有所幫助。
-
想要看懂動(dòng)態(tài)代理所需要具備的知識(shí):
- 需要對(duì) java 語法有基本的了解
- 需要初步具備對(duì)面向?qū)ο缶幊趟枷氲脑O(shè)計(jì)思想
- 了解多態(tài)的概念。
-
什么是代理模式?
代理模式 (Proxy Pattern) :給某一個(gè)對(duì)象提供一個(gè)代 理,并由代理對(duì)象控制對(duì)原對(duì)象的引用。代理模式的英 文叫 做Proxy 或 Surrogate,它是一種對(duì)象結(jié)構(gòu)型模式。這里給出一個(gè)例子:假設(shè)現(xiàn)在有 Tank 這個(gè)類,其繼承了 Moveable 接口,里面有一個(gè) move() 方法,現(xiàn)在需要統(tǒng)計(jì)這個(gè)方法的運(yùn)行時(shí)間,要求不能改變這個(gè)類的源代碼,問有幾種實(shí)現(xiàn)方式?
moveable 接口 :
package proxy; public interface Moveable { void move() ; }
Tank 類:
package proxy; public class Tank implements Moveable { @Override public void move() throws InterruptedException { System.out.println("Tank moving ..."); Thread.sleep(10000); } }
答: 聚合 and 繼承
- 繼承:新建一個(gè)類 Tank1 繼承 Tank,重寫其 move() 方法:
package proxy; public class Tank1 extends Tank { @Override public void move() throws InterruptedException { long startTime = System.currentTimeMillis() ; super.move(); long endTime = System.currentTimeMillis() ; System.out.println("time : " + (endTime - startTime)); } }
- 聚合:新建一個(gè)類 Tank2,也實(shí)現(xiàn) Moveable 接口,定義一個(gè) Moveable 的實(shí)例變量,調(diào)用其 move() 方法,并添加代碼
package proxy; public class Tank2 implements Moveable{ public Tank2(Moveable m) { this.m = m; } private Moveable m; @Override public void move() throws InterruptedException { long startTime = System.currentTimeMillis() ; m.move(); long endTime = System.currentTimeMillis() ; System.out.println("time : " + (endTime - startTime)); } }
以上的代碼就是初步的代理模式。即在不改變被代理對(duì)象的前提下,對(duì)其里面的方法或字段進(jìn)行加工處理。
-
那么問題來了,兩種代理模式哪種比較好?
現(xiàn)在再給出一個(gè)需求,我們需要在統(tǒng)計(jì) Tank 對(duì)象方法運(yùn)行時(shí)間的基礎(chǔ)上,再加一個(gè)記錄方法運(yùn)行的日志代理,而后再改變兩個(gè)代理的前后順序。-
繼承實(shí)現(xiàn):
- 添加一個(gè)記錄日志代理:再新建一個(gè) Tank3 類,繼承 Tank1 類,再重寫其 move() 方法
- 改變兩個(gè)代理的前后順序:再新建一個(gè) Tank4 類,繼承 Tank 類,先重寫 move() 方法添加日志代理,再新建一個(gè) Tank5 類,繼承 Tank3 類,再重寫 move() 方法添加時(shí)間代理。以此類推。
-
聚合實(shí)現(xiàn):
- 再添加一個(gè)日志代理:新建一個(gè) Tank3 類,實(shí)現(xiàn) Moveable 接口,定義一個(gè) Moveable 類型變量,把代理過的 Tank1 對(duì)象傳進(jìn)來,調(diào)用其 move() 方法。
public class Tank6 implements Moveable { public Tank6(Moveable m) { this.m = m; } private Moveable m ; @Override public void move() throws InterruptedException { System.out.println("Log start..."); m.move(); System.out.println("Log end ..."); } }
這里給一下客戶端 Client 的代碼及運(yùn)行結(jié)果:
運(yùn)行結(jié)果- 改變兩個(gè)代理的順序:先把被代理的 tank 對(duì)象傳給 Tank6 實(shí)現(xiàn)日志代理,得到 tankLogProxy 對(duì)象,再將其傳給 Tank2 實(shí)現(xiàn)時(shí)間代理。
這里給一下客戶端 Client 的代碼及運(yùn)行結(jié)果:
運(yùn)行結(jié)果
到了這里,相信各位已經(jīng)能看出來了。代理模式的實(shí)現(xiàn),還是聚合的方法比較好,特別是在改變代理順序的需求下,只需要改變客戶端的代理順序即可,可以避免類的無限擴(kuò)增。
-