Head First 设计模式 —— 13. 代理 (Proxy) 模式
思考題
如何設計一個支持遠程方法調用的系統?你要怎樣才能讓開發人員不用寫太多代碼?讓遠程調用看起來像本地調用一樣,毫無瑕疵? P435
- 已經接觸過 RPC 了,所以就很容易知道具體流程:客戶端調用目標類的代理對象(消費者)的方法,消費者內部將相關調用信息通過網絡傳到服務端對應的目標類的代理對象(生產者)中,生產者解析調用信息,然后真正去調用目標類的實際對象,并將返回結果回傳給消費者,消費者再返回給客戶端。 RPC 框架使用代理模式使得內部一系列處理及信息傳輸等對客戶端和服務端是透明的,客戶端會認為實際是本地調用一樣,不知道調用了遠程方法;服務端也不知道是在給遠程對象提供服務。
思考題
遠程調用程序應該完全透明嗎?這是個好主意嗎?這個方法可能會產生什么問題? P435
- 遠程調用程序不應該完全透明。由于引入了網絡通信和數據處理(序列化、反序列化和壓縮等),可能在相關過程會異常,客戶端應該知曉并處理這些異常,而不應該讓 RPC 框架消化掉這些異常而返回一些默認值。
代理模式
為另一個對象提供一個替身或占位符以控制對這個對象的訪問。 P460
特點
- 代理控制訪問
- 遠程代理:控制訪問遠程對象 P460
- 虛擬代理:控制訪問創建開銷大的資源 P460
- 保護代理:基于權限控制對資源的訪問 P460
- 動態代理:在運行時動態地創建一個代理類,并將方法的調用轉發到指定的類 P474
- 防火墻代理:控制網絡資源的訪問,保護主題免于“壞客戶”的侵害。多用于防火墻系統 P488
- 智能引用代理:當主題被引用時,進行額外的動作,例如計一個對象被引用的次數。可用于對某些操作的日志記錄 P488
- 緩存代理:為開銷大的運算結果提供暫時存儲;也允許多個客戶共享結果,以減少計算或網絡延遲。多用于 Web 服務器代理,以及內容管理與出版系統 P488
- 同步代理:在多線程的情況下為主題提供安全的訪問。可用于 JavaSpaces ,為分散式環境內的潛在對象集合提供同步訪問控制 P489
- 復雜隱藏代理:用來隱藏一個類的復雜集合的復雜度,并進行訪問控制;有時候也成為外觀代理,但與外觀模式不同,因為代理控制訪問,而外觀模式只提供另一組接口 P489
- 寫入時復制代理:用來控制對象的復制,方法是延遲對象的復制,直到客戶真的需要為止。這是虛擬代理的變體。CopyOnWriteArrayList 使用這種方式 P489
缺點
- 代理會造成設計中類的數目增加 P491
代理模式和裝飾器模式 P471
- 代理模式控制對象的訪問
- 裝飾器模式為對象增加行為
代理模式和適配器模式的區別 P471
- 代理模式實現相同的接口(保護代理可能只提供給客戶部分接口,與某些適配器很像)
- 適配器模式改變對象適配的接口
思考題
class ImageProxy implements Icon {// 實例變量構造器在這里public int getIconWidth() {if (imageIcon != null) {return imageIcon.getIconWidth();} else {return 800;}}public int getIconHeight() {if (imageIcon != null) {return imageIcon.getIconHeight();} else {return 600;}}public void paintIcon(final Component c, Graphics g, int y, int y) {if (imageIcon != null) {imageIcon.paintIcon(c, g, x, y);} else {g.drawString("Loading CD cover, please wait...", x + 300, y + 190);// 實例化 imageIcon 獲取圖片}} }以上為 CD 封面虛擬代理, ImageProxy 類似乎有兩個,由條件語句控制的狀態。你能否用另一個設計模式清理這樣的代碼?你要如何重新設計 ImageProxy ? P468
- 可以用狀態模式清理掉條件語句。設置兩個狀態 ImageNotLoaded 和 ImageLoaded ,分別將各個方法內條件語句內的代碼放入這個兩個狀態的對應方法中,初始狀態是 ImageNotLoaded ,當第一次調用 paintIcon 方法時,開始實例化 imageIcon 獲取圖片,成功后設置狀態為 ImageLoaded 。
思考題
NonOwnerInvocationHandler 工作的方式除了它允許調用 setHotOrNotRating() 和不允許調用其他 set 方法之外,與 NonOwnerInvocationHandler 是很相似的。請寫出 NonOwnerInvocationHandler 的代碼: P482
import java.lang.reflect.*;public class NonOwnerInvocationHandler implements InvocationHandler {PersonBean person;public NonOwnerInvocationHandler(PersonBean person) {this.person = person;}public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException {try {String methodName = method.getName();if (methodName.startsWith("get")) {return method.invoke(person, args);} else if(methodName.equals("setHotOrNotRating")) {return method.invoke(person, args);} else if(methodName.startsWith("set")) {throw new IllegalAccessException();}} catch (IllegalAccessException e) {e.printStackTrace();}return null;} }思考題
創建動態代理所需要的代碼很短,請你寫下 getNonOwnerProxy() ,該方法會返回 NonOwnerInvocationHandler 的代理。更進一步,請寫下 getProxy() 方法,參數是 handler 和 person ,返回值是使用此 handler 的代理。 P483
PersonBean getNonOwnerProxy(PersonBean person) {return (PersonBean) Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), new NonOwnerInvocationHandler(person)); }PersonBean getProxy(InvocationHandler handler, PersonBean person) {return (PersonBean) Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), handler); }思考題
如何知道某個類是不是代理類? P486
- JDK 動態代理的類是 Proxy 的子類,有一個靜態方法 isProxyClass() ,此方法的返回值如果為 true ,表示這是一個動態代理類。
- 【存疑】 代理類還會實現特定的某些接口
- 在 Java8 中調用 proxy.getClass().getInterfaces() 及其他與獲取接口有關的方法,并未發現實現新接口
思考題
能傳入 newProxyInstance() 的接口類型,有沒有什么限制?
- 傳入的接口數組內只能有接口,不能有類 P486
- 如果接口不是 public ,就必須屬于同一個 package P486
- 【存疑】 不同的接口內,不可以有名稱和參數完全一樣的方法 P486
- 經過 Java8 中實踐確認沒有此限制,不過永遠只會識別為其中一個接口(接口數組內第一次出現該方法的接口)的方法
- 接口數組內的接口可以不是被代理類實現的接口,代理類實現了接口數組內的所有接口,所有接口的調用都可以被攔截處理
- 被代理類可以不實現任何接口,自己指定接口和相關調用處理邏輯也能使用
思考題
配對下列模式和描述: P487
代理模式:包裝另一個對象,并控制對它的訪問
外觀模式:包裝許多對象以簡化它們的接口
裝飾器模式:包裝另一個對象,并提供額外的行為
適配器模式:包裝另一個對象,并提供不同的接口
所思所想
- 以前也看過不同動態代理的實現,但只是走馬觀花式地看一遍如何實現,沒有實際去動手,這次讀書時實際動手后感覺理解更深入了一點,大概更能了解內部是如何流轉的
- 實踐是檢驗真理的唯一標準。書中存在部分說明可能錯誤或者不適用于當前版本,要利用好身邊的工具,多實踐操作,不能盡信書
本文首發于公眾號:滿賦諸機(點擊查看原文) 開源在 GitHub :reading-notes/head-first-design-patterns
總結
以上是生活随笔為你收集整理的Head First 设计模式 —— 13. 代理 (Proxy) 模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PostgreSQL下载安装教程(及Po
- 下一篇: 网站关键词挖掘技术