一篇學會常見的代理模式
1. 代理模式概述
2. 代理模式的結構與實現
3. 代理模式的應用實例
4. 遠程代理
5. 虛擬代理
6. Java動態代理
7. 代理模式的優缺點與適用環境
“Github:https://github.com/nateshao/design-demo/tree/main/JavaDesignPatterns/15-proxy
1. 代理模式概述
相信大家都聽過代理模式,有靜態代理,JDK動態代理,Cglib代理(Spring的內容)。接下來,千羽和大家一起學習一下這些代理模式各有優缺點和相應的使用場景。
商品代購示意圖:
分析
- 代購商品:顧客 -> 代購網站 -> 商品
- 軟件開發:客戶端 -> 代理對象 -> 真實對象
還有這種類型
定義:
代理模式:給某一個對象提供一個代理或占位符,并由代理對象來控制對原對象的訪問。
- 引入一個新的代理對象
- 代理對象在客戶端對象和目標對象之間起到中介的作用
- 去掉客戶不能看到的內容和服務或者增添客戶需要的額外的新服務
代理模式的結構
代理模式包含以下3個角色:
- Subject(抽象主題角色)
- Proxy(代理主題角色)
- RealSubject(真實主題角色)
2. 代理模式的結構與實現
抽象主題類典型代碼:
- public abstract class Subject {
- public abstract void request();
- }
真實主題類典型代碼:
- public class RealSubject extends Subject{
- public void request() {
- //業務方法具體實現代碼
- }
- }
代理類典型代碼:
- public class Proxy extends Subject {
- private RealSubject realSubject = new RealSubject(); //維持一個對真實主題對象的引用
- public void preRequest() {
- …...
- }
- public void request() {
- preRequest();
- realSubject.request(); //調用真實主題對象的方法
- postRequest();
- }
- public void postRequest() {
- ……
- }
- }
幾種常見的代理模式
- 遠程代理(Remote Proxy):為一個位于不同的地址空間的對象提供一個本地的代理對象,這個不同的地址空間可以在同一臺主機中,也可以在另一臺主機中,遠程代理又稱為大使(Ambassador)
- 虛擬代理(Virtual Proxy):如果需要創建一個資源消耗較大的對象,先創建一個消耗相對較小的對象來表示,真實對象只在需要時才會被真正創建
- 保護代理(Protect Proxy):控制對一個對象的訪問,可以給不同的用戶提供不同級別的使用權限
- 緩沖代理(Cache Proxy):為某一個目標操作的結果提供臨時的存儲空間,以便多個客戶端可以共享這些結果
- 智能引用代理(Smart Reference Proxy):當一個對象被引用時,提供一些額外的操作,例如將對象被調用的次數記錄下來等
3. 代理模式的應用實例
某軟件公司承接了某信息咨詢公司的收費商務信息查詢系統的開發任務,該系統的基本需求如下:
在進行商務信息查詢之前用戶需要通過身份驗證,只有合法用戶才能夠使用該查詢系統;
在進行商務信息查詢時系統需要記錄查詢日志,以便根據查詢次數收取查詢費用。
該軟件公司開發人員已完成了商務信息查詢模塊的開發任務,現希望能夠以一種松耦合的方式向原有系統增加身份驗證和日志記錄功能,客戶端代碼可以無區別地對待原始的商務信息查詢模塊和增加新功能之后的商務信息查詢模塊,而且可能在將來還要在該信息查詢模塊中增加一些新的功能。
現使用代理模式設計并實現該收費商務信息查詢系統。
實例分析及類圖:
商務信息查詢系統設計方案示意圖
商務信息查詢系統結構圖
實例代碼
- AccessValidator:身份驗證類,業務類
- Logger:日志記錄類,業務類
- Searcher:抽象查詢類,充當抽象主題角色
- RealSearcher:具體查詢類,充當真實主題角色
- ProxySearcher:代理查詢類,充當代理主題角色
- Client:客戶端測試類
結果分析
- 保護代理和智能引用代理
- 在代理類ProxySearcher中實現對真實主題類的權限控制和引用計數
4. 遠程代理
動機
- 客戶端程序可以訪問在遠程主機上的對象,遠程主機可能具有更好的計算性能與處理速度,可以快速地響應并處理客戶端的請求
- 可以將網絡的細節隱藏起來,使得客戶端不必考慮網絡的存在
- 客戶端完全可以認為被代理的遠程業務對象是在本地而不是在遠程,而遠程代理對象承擔了大部分的網絡通信工作,并負責對遠程業務方法的調用
結構
5. 虛擬代理
動機
對于一些占用系統資源較多或者加載時間較長的對象,可以給這些對象提供一個虛擬代理
在真實對象創建成功之前虛擬代理扮演真實對象的替身,而當真實對象創建之后,虛擬代理將用戶的請求轉發給真實對象
使用一個“虛假”的代理對象來代表真實對象,通過代理對象來間接引用真實對象,可以在一定程度上提高系統的性能
應用
由于對象本身的復雜性或者網絡等原因導致一個對象需要較長的加載時間,此時可以用一個加載時間相對較短的代理對象來代表真實對象(結合多線程技術)
一個對象的加載十分耗費系統資源,讓那些占用大量內存或處理起來非常復雜的對象推遲到使用它們的時候才創建,而在此之前用一個相對來說占用資源較少的代理對象來代表真實對象,再通過代理對象來引用真實對象(用時間換取空間)
6. Java動態代理
- 動態代理(Dynamic Proxy)可以讓系統在運行時根據實際需要來動態創建代理類,讓同一個代理類能夠代理多個不同的真實主題類而且可以代理不同的方法
- Java語言提供了對動態代理的支持,Java語言實現動態代理時需要用到位于java.lang.reflect包中的一些類
Proxy類
- public static Class< ? > getProxyClass(ClassLoader loader, Class... interfaces):該方法用于返回一個Class類型的代理類,在參數中需要提供類加載器并需要指定代理的接口數組(與真實主題類的接口列表一致)
- public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):該方法用于返回一個動態創建的代理類的實例,方法中第一個參數loader表示代理類的類加載器,第二個參數interfaces表示代理類所實現的接口列表(與真實主題類的接口列表一致),第三個參數h表示所指派的調用處理程序類
InvocationHandler接口
- InvocationHandler接口是代理處理程序類的實現接口,該接口作為代理實例的調用處理者的公共父類,每一個代理類的實例都可以提供一個相關的具體調用處理者(InvocationHandler接口的子類)
- public Object invoke(Object proxy, Method method, Object[] args):該方法用于處理對代理類實例的方法調用并返回相應的結果,當一個代理實例中的業務方法被調用時將自動調用該方法。invoke()方法包含三個參數,其中第一個參數proxy表示代理類的實例,第二個參數method表示需要代理的方法,第三個參數args表示代理方法的參數數組
- 動態代理類需要在運行時指定所代理真實主題類的接口,客戶端在調用動態代理對象的方法時,調用請求會將請求自動轉發給InvocationHandler對象的invoke()方法,由invoke()方法來實現對請求的統一處理。
動態代理實例
“某軟件公司欲為公司OA系統數據訪問層DAO增加方法調用日志,記錄每一個方法被調用的時間和調用結果,現使用動態代理進行設計和實現。
實例代碼
- AbstractUserDAO:抽象用戶DAO類,抽象主題角色
- AbstractDocumentDAO:抽象文檔DAO類,抽象主題角色
- UserDAO:用戶DAO類,具體主題角色
- DocumentDAO:文檔DAO類,具體主題角色
- DAOLogHandler:自定義請求處理程序類
- Client:客戶端測試類
7. 代理模式的優缺點與適用環境
模式優點
- 能夠協調調用者和被調用者,在一定程度上降低了系統的耦合度
- 客戶端可以針對抽象主題角色進行編程,增加和更換代理類無須修改源代碼,符合開閉原則,系統具有較好的靈活性和可擴展性
模式優點——逐個分析
- 遠程代理:可以將一些消耗資源較多的對象和操作移至性能更好的計算機上,提高了系統的整體運行效率
- 虛擬代理:通過一個消耗資源較少的對象來代表一個消耗資源較多的對象,可以在一定程度上節省系統的運行開銷
- 緩沖代理:為某一個操作的結果提供臨時的緩存存儲空間,以便在后續使用中能夠共享這些結果,優化系統性能,縮短執行時間
- 保護代理:可以控制對一個對象的訪問權限,為不同用戶提供不同級別的使用權限
模式缺點
由于在客戶端和真實主題之間增加了代理對象,因此有些類型的代理模式可能會造成請求的處理速度變慢(例如保護代理)
實現代理模式需要額外的工作,而且有些代理模式的實現過程較為復雜(例如遠程代理)
模式適用環境
當客戶端對象需要訪問遠程主機中的對象時可以使用遠程代理
當需要用一個消耗資源較少的對象來代表一個消耗資源較多的對象,從而降低系統開銷、縮短運行時間時可以使用虛擬代理
當需要為某一個被頻繁訪問的操作結果提供一個臨時存儲空間,以供多個客戶端共享訪問這些結果時可以使用緩沖代理
當需要控制對一個對象的訪問,為不同用戶提供不同級別的訪問權限時可以使用保護代理
當需要為一個對象的訪問(引用)提供一些額外的操作時可以使用智能引用代理