最近看了馬士兵老師的設(shè)計模式視頻挡逼,感覺其中最難也最感興趣的就是代理模式了驶沼。馬士兵老師從靜態(tài)代理的兩種基本方式出發(fā)夯秃,到初步實現(xiàn)指定接口的動態(tài)代理,最后到模仿 JDK 的動態(tài)代理苇经,講的都很詳細。現(xiàn)在我來給大家梳理一下整個思路宦言,希望對大家有所幫助扇单。
-
想要看懂動態(tài)代理所需要具備的知識:
- 需要對 java 語法有基本的了解
- 需要初步具備對面向?qū)ο缶幊趟枷氲脑O(shè)計思想
- 了解多態(tài)的概念。
-
什么是代理模式奠旺?
代理模式 (Proxy Pattern) :給某一個對象提供一個代 理蜘澜,并由代理對象控制對原對象的引用施流。代理模式的英 文叫 做Proxy 或 Surrogate,它是一種對象結(jié)構(gòu)型模式鄙信。這里給出一個例子:假設(shè)現(xiàn)在有 Tank 這個類,其繼承了 Moveable 接口瞪醋,里面有一個 move() 方法,現(xiàn)在需要統(tǒng)計這個方法的運行時間装诡,要求不能改變這個類的源代碼银受,問有幾種實現(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 繼承
- 繼承:新建一個類 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)); } }
- 聚合:新建一個類 Tank2,也實現(xiàn) Moveable 接口,定義一個 Moveable 的實例變量鸦采,調(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)); } }
以上的代碼就是初步的代理模式。即在不改變被代理對象的前提下赖淤,對其里面的方法或字段進行加工處理蜀漆。
-
那么問題來了,兩種代理模式哪種比較好咱旱?
現(xiàn)在再給出一個需求确丢,我們需要在統(tǒng)計 Tank 對象方法運行時間的基礎(chǔ)上,再加一個記錄方法運行的日志代理,而后再改變兩個代理的前后順序吐限。-
繼承實現(xiàn):
- 添加一個記錄日志代理:再新建一個 Tank3 類鲜侥,繼承 Tank1 類,再重寫其 move() 方法
- 改變兩個代理的前后順序:再新建一個 Tank4 類诸典,繼承 Tank 類描函,先重寫 move() 方法添加日志代理,再新建一個 Tank5 類狐粱,繼承 Tank3 類舀寓,再重寫 move() 方法添加時間代理。以此類推肌蜻。
-
聚合實現(xiàn):
- 再添加一個日志代理:新建一個 Tank3 類互墓,實現(xiàn) Moveable 接口,定義一個 Moveable 類型變量蒋搜,把代理過的 Tank1 對象傳進來篡撵,調(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 的代碼及運行結(jié)果:
運行結(jié)果- 改變兩個代理的順序:先把被代理的 tank 對象傳給 Tank6 實現(xiàn)日志代理豆挽,得到 tankLogProxy 對象育谬,再將其傳給 Tank2 實現(xiàn)時間代理。
這里給一下客戶端 Client 的代碼及運行結(jié)果:
運行結(jié)果
到了這里帮哈,相信各位已經(jīng)能看出來了膛檀。代理模式的實現(xiàn),還是聚合的方法比較好,特別是在改變代理順序的需求下宿刮,只需要改變客戶端的代理順序即可互站,可以避免類的無限擴增。
-