【再探】設計模式—代理模式

?代理是指授權代理人在一定范圍內代表其向第三方進行處理有關事務。

1 代理模式

需求:1)將業務代碼與非業務代碼分離,在不改變代碼結構的基礎上,為其添加新的功能。2)為系統中的某些操作做同一處理,例如進行鑒權、監控、日志、統計等。

1.1 代理模式介紹

增加一個代理對象,在客戶端和目標對象之間起到中介作用,去掉客戶不能看到的內容和服務,或者添加客戶想要的額外服務。

圖 代理模式UML

public class StaticProxy {public static void main(String[] args) {Subject request = new SimpleRequest();Subject proxy = new LogProxy(request);proxy.request("applet","123456");}private interface Subject {void request(String source,String token);}private static class LogProxy implements Subject {private final Subject subject;private LogProxy(Subject subject) {this.subject = subject;}@Overridepublic void request(String source, String token) {System.out.println("記錄訪問日志,source=" + source + ",token=" + token);subject.request(source,token);}}private static class SimpleRequest implements Subject {@Overridepublic void request(String source, String token) {System.out.println("接收請求:" + source + "," + token);}}}

代理模式

側重控制對目標對象的訪問,及擴展不同于目標對象緯度的功能。

裝飾模式

側重于增強目標對象本身的功能,是繼承方案的一個代替。

表 代理模式與裝飾模式對比

1.1.1 動態代理

靜態代理模式,需要為每個目標對象創建一個代理類,會造成系統中類的數量增多,而且當這些代理類提供的功能相同時,將會造成代碼的冗余。動態代理是指在運行時動態創建代理對象。

JDK

JDK自帶的一種動態代理方法,要求被代理類必須要實現接口。

原理:動態生成實現目標接口的代理類,調用代理方法時,通過反射的形式來調用目標對象方法。

CGLIB

是CGLIB提供的一種動態代理方法。被代理類可為普通類及接口。

原理:動態生成繼承目標類的代理類,通過fastclass 的策略來找到目標方法。

與JDK方式對比,其動態生成的類會比較多,但執行效率會比反射更快。

表 Java中兩種動態代理

JDK 方式:

public class JdkDynamicProxy {private interface Subject {void request(String source,String token);}private static class RealRequest implements Subject{@Overridepublic void request(String source, String token) {System.out.println("請求網絡");}}private static class RequestIntercept implements InvocationHandler {private final Subject subject;private RequestIntercept(Subject subject) {this.subject = subject;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("請求參數:" + Arrays.asList(args));Object result = method.invoke(subject, args);System.out.println("記錄訪問日志");return result;}}public static void main(String[] args) {Subject realSubject = new RealRequest();Subject subject = (Subject) Proxy.newProxyInstance(JdkDynamicProxy.class.getClassLoader(), new Class[]{Subject.class}, new RequestIntercept(realSubject));subject.request("小程序","1344");}}

CGLIB方式

public class CglibDynamicProxy {private static class RealRequest {public RealRequest() {}public void request(String source, String token) {System.out.println("請求成功,source=" + source + ",token=" + token);}}private static class RequestIntercept implements MethodInterceptor {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("攔截請求:" + Arrays.asList(objects));Object result = methodProxy.invokeSuper(o, objects);System.out.println("記錄請求日志");return result;}}public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(RealRequest.class);enhancer.setCallback(new RequestIntercept());RealRequest realRequest = (RealRequest) enhancer.create();realRequest.request("小程序","1344");}
}

1.1.2 遠程代理

使得客戶端可以訪問遠程機,將網絡細節隱藏起來,客戶端可完全認為被代理的遠程對象是局域的而非遠程的。

遠程代理對象承擔了大部份網絡通信工作,并負責對遠程業務方法的調用。

RMI,Remote Method Invocation,遠程方法調用,是JDK 實現的一種遠程代理。

表 RMI 內部實現步驟

客戶端通過一個樁(Stub),相當于代理類對象,與遠程主機上的業務對象進行通信。而遠程主機端有個Skeleton(骨架)對象來負責與Stub對象通信。

遠程機部署代碼:

public interface RemoteService extends Remote {String service(String requestInfo) throws RemoteException;}public class RemoteServiceImpl extends UnicastRemoteObject implements RemoteService{public RemoteServiceImpl() throws RemoteException{}@Overridepublic String service(String requestInfo) throws RemoteException {System.out.println("請求:" + requestInfo);return "RemoteService提供遠程服務:" + requestInfo;}
}public class JNDIPublisher {public static void main(String[] args) throws RemoteException, MalformedURLException {RemoteService remoteService = new RemoteServiceImpl();int port = 8080;String serviceName = "rmi://localhost:" + port + "/remoteService";LocateRegistry.createRegistry(port);Naming.rebind(serviceName,remoteService);}}

客戶端代碼:

public class RmiLocal {public static void main(String[] args) throws MalformedURLException, NotBoundException, RemoteException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {String serviceName = "rmi://localhost:" + 8080 + "/remoteService";reflectRemote(serviceName);interfaceRemote(serviceName);}// 這種方式仍然需要導入遠程服務提供的接口,RemoteServiceprivate static void reflectRemote(String serviceName) throws MalformedURLException, NotBoundException, RemoteException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {Remote lookup = Naming.lookup(serviceName);Method method = lookup.getClass().getMethod("service", String.class);Object result = method.invoke(lookup, "反射方式");System.out.println("反射方式,遠程調用結果:" + result);}private static void interfaceRemote(String serviceName) throws MalformedURLException, NotBoundException, RemoteException{RemoteService remoteService = (RemoteService)Naming.lookup(serviceName);String result = remoteService.service("接口方式");System.out.println("接口方式,遠程調用結果:" + result);}}

1.1.3 虛擬代理

主要目標是延遲對象的創建或者復雜計算,直到它們實際需要被創建或計算。這可以提高性能,降低資源消耗。

public class VirtualAgent {private interface Image {void display();}private static class RealImage implements Image {private String path;public RealImage(String path) {System.out.println("創建一個圖片需要許多資源,耗費時間較長");this.path = path;}@Overridepublic void display() {System.out.println("展示圖片:" + path);}}private static class ProxyImage implements Image {private volatile Image image;private String path;public ProxyImage(String path) {System.out.println("虛擬對象,需要資源及時間都很少,用于延遲真實對象的創建與訪問");this.path = path;}@Overridepublic void display() {if (image == null) {synchronized (ProxyImage.class) {if (image == null) image = new RealImage(path);}}image.display();}}public static void main(String[] args) {Image[] images = new Image[10];// 在網頁加載過程,先加載這些圖片,為了防止陷入卡頓,不直接創建圖片,而是先創建虛擬對象System.out.println("網頁加載中...");for (int i = 0; i < images.length; i++) {images[i] = new ProxyImage(i + ".png");}System.out.println("網頁加載完成--------");// 當網頁加載完成后,可以依次加載真實圖片了for (Image image : images) {image.display();}}}

1.1.4 緩存代理

為某些目標操作的結果提供臨時的存儲空間,讓后續相同的操作及相同的參數可以直接共享這些結果,而不必再執行計算,以此來提高系統性能。

public class CacheProxy {private static class CacheMethodInterceptor implements MethodInterceptor {private final static Map<MethodKey,Object> resultMap = new HashMap<>();@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {MethodKey methodKey = MethodKey.getKey(method, args);Object result = resultMap.get(methodKey);if (result != null) {return result;} else {Object result2 = methodProxy.invokeSuper(o, args);resultMap.put(methodKey,result2);return result2;}}}// 根據method 及 參數來生成keyprivate static class MethodKey {private static final Map<String,MethodKey> methodKeyMap = new HashMap<>();private String code;private MethodKey(String code) { this.code = code;}public static MethodKey getKey(Method method, Object[] objects) {String hasCode = "method:" + method.hashCode();if (objects != null) hasCode += "args:" + Arrays.hashCode(objects);MethodKey methodKey = methodKeyMap.get(hasCode);if (methodKey == null) {synchronized (MethodKey.class) {methodKey = new MethodKey(hasCode);methodKeyMap.put(hasCode,methodKey);}}return methodKey;}@Overridepublic String toString() {return code;}}private static class ComplexCompute {public ComplexCompute() {}long compute(long num) {System.out.println("執行復雜計算,參數" + num);return num * 2345;}}public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(ComplexCompute.class);enhancer.setCallback(new CacheMethodInterceptor());ComplexCompute compute = (ComplexCompute) enhancer.create();System.out.println(compute.compute(23));System.out.println("----------");System.out.println(compute.compute(123));System.out.println("----------");System.out.println(compute.compute(23));}}

1.2 優缺點

優點:

  1. 能動態擴展對象的功能,而不需要修改原有代碼,符合開閉原則。
  2. 能對目標對象的方法訪問進行控制與保護。
  3. 將耗費系統資源及時間較多的對象延遲到真正需要使用時再創建,能提高系統性能及系統對客戶的友好度。
  4. 能調用遠程對象方法,而不需要考慮復雜的實現細節。
  5. 能對運行結果進行緩存,提高系統運行效率。

缺點:

  1. 增加了代理對象,可能會造成請求的處理速度變慢。
  2. 增加了類的數量,讓系統變得更復雜。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/14955.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/14955.shtml
英文地址,請注明出處:http://en.pswp.cn/web/14955.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

[實例] Unity Shader 逐像素漫反射與半蘭伯特光照

漫反射光照是Unity中最基本最簡單的光照模型&#xff0c;本篇將會介紹在片元著色器中實現反射效果&#xff0c;并會采用半蘭伯特光照技術對其進行改進。 1. 逐頂點光照與逐像素光照 在Unity Shader中&#xff0c;我們可以有兩個地方可以用來計算光照&#xff1a;在頂點著色器…

數據結構:帶頭雙向循環鏈表

目錄 前言 鏈表實現 1.定義節點 2.接口實現 1.開辟新節點 2.初始化 3.打印鏈表 4.添加節點 頭插 尾插 在pos位置之前增加節點 5.刪除節點 判空 頭刪 尾刪 刪除pos位置的節點 6.查找 7.釋放 前言 帶頭雙向循環鏈表的結構最復雜&#xff0c;一般用在單獨存儲數…

z3-加法器實驗

補碼器加減法&#xff0c;運算方法簡介 我們要知道什么是補碼的加法&#xff0c;我們為什么要用補碼的加法&#xff1f; 補碼的加法其實就是將兩個補碼形式的二進制數字直接相加&#xff0c;處理的時候忽略超出固定位數的進位。補碼的加法運算和無符號二進制數的加法操作一樣&…

【最新區塊鏈論文錄用資訊】CCF A — SP 2024 共17篇

Conference&#xff1a;45th IEEE Symposium onSecurity and Privacy CCF level&#xff1a;CCF A Categories&#xff1a;網絡與信息安全 Year&#xff1a;2024 Num&#xff1a;17 Efficient Zero-Knowledge Arguments For Paillier Cryptosystem Paillier 加密系統的有效…

基于python的網頁自動刷新工具

1.下載webdriver https://msedgewebdriverstorage.z22.web.core.windows.net/?prefix122.0.2365.59/下載Edge的瀏覽器驅動 2.安裝selenium pip install selenium4.11.1 3.寫代碼 # -*- coding: utf-8 -*- import tkinter as tk from tkinter import messagebox import thr…

【halcon】set_part 實現平移和縮放 徹悟版

背景 之前寫了一篇關于set_part 的文章 &#xff0c;確實也實現了平移和縮放。平移是對的&#xff0c;但是縮放其實有畸變。這個問題一直都困擾著我&#xff0c;知道昨天連續測試了好幾個小時&#xff0c;直到晚上11點終于完美解決。 坐標和高寬 坐標 再講set_part 之前&am…

免費擼gpt-4o和各種大模型實用經驗分享

項目 Github: https://github.com/MartialBE/one-api 先貼兩張圖&#xff1a; 說明 免費擼AI大模型,各位可以對照下面我給出的大模型記錄表來填&#xff0c;key需要自己去拿&#xff0c;國內都需要手機號驗證&#xff0c;如果你不介意。另外我在自己的博客放出免費API給大家…

模型評價指標筆記:混淆矩陣+F1+PR曲線+mAP

評價指標 二分類評價指標 混淆矩陣 TP: 正確預測為了正樣本&#xff0c;原來也是正樣本 FN: 錯誤的預測為負樣本&#xff0c;原來是正樣本 (漏報&#xff0c;沒有找到正確匹配的數目) FP: 錯誤的預測為正樣本&#xff0c;原來是負樣本 (誤報&#xff0c;沒有的匹配不正確) TN…

CIM模型

CIM 是 Esri 制圖信息模型。 它是一個地圖內容規范,用于記錄在保存、讀取、引用或打開時如何永久保留描述不同項目組件的信息。 該規范以 JSON 表示,適用于 ArcGIS 應用程序和 API 中的地圖、場景、布局、圖層、符號和樣式。 CIM 不僅限于制圖設置。 要了解屬性的組織方式以及…

【Tools】SpringBoot工程中,對于時間屬性從后端返回到前端的格式問題

Catalog 時間屬性格式問題一、需求二、怎么使用 時間屬性格式問題 一、需求 對于表中時間字段&#xff0c;后端創建對應的實體類的時間屬性需要設定格式&#xff08;默認的格式不方便閱讀&#xff09;&#xff0c;再返回給前端。 二、怎么使用 導入jackson相關的坐標&#x…

Vue.js - Vue 的安裝 以及 常用的 Vue 指令 【0基礎向 Vue 基礎學習】

文章目錄 Vue 快速上手1、Vue.js 官網 & Vue.js 的獲取2、創建 Vue 實例&#xff0c;初始化渲染3、插值表達式 安裝 Vue 開發者工具&#xff1a;裝插件調試 Vue 應用Vue 指令1、v-show 指令2、v-if3、v-else & v-else-if4、v-onv-on 調用傳參 5、v-bindv-bind 對于樣式…

【算法】前綴和算法——和為k的子數組之和

題解&#xff1a;和為k的子數組之和(前綴和算法) 目錄 1.題目2.題解思路2.1前綴和 哈希表&#xff0c;算法步驟&#xff1a;2.2細節如下&#xff1a;2.3參考代碼&#xff1a; 3.總結及思考 1.題目 題目鏈接&#xff1a;LINK 2.題解思路 暴力求解自然不用多說&#xff0c;時…

【SQL】外連接 LEFT JOIN

目錄 一.內連接與外連接 1.內連接&#xff08;inner join&#xff09; 2.外連接&#xff08;outer join&#xff09; 二.兩表連接 1.我們先來試試看內連接&#xff1a; 2.我們再來試試外連接 三.單表外連接 四.總結 一.內連接與外連接 先得介紹內連接和外連接兩個概念&…

第199題|關于函數的周期性問題|函數強化訓練(六)|武忠祥老師每日一題 5月24日

解題思路&#xff1a;解這道題我們要用到下面這個結論 f(x)連續&#xff0c;以T為周期時&#xff0c;原函數以T為周期的充分必要條件是&#xff1a; (A) sin x顯然是以π為周期的&#xff0c;我們可以看到并不等于0,根據結論&#xff0c;A的原函數顯然不是周期函數。 (B) 的…

memmove使?和模擬實現

一&#xff1a;memmove的使? 這是memmove在庫里的定義&#xff0c;具體可在cplusplus.com查看 void * memmove ( void * destination, const void * source, size_t num ) ? 和memcpy的差別就是memmove函數處理的源內存塊和?標內存塊是可以重疊的。 ? 如果源空間和?標…

你以為的私域是真正的私域嘛??你的私域流量真的屬于你嘛?

大家好 我是一個軟件開發公司的產品經理 專注私域電商行業7年有余 您的私域流量是真正的屬于你自己嘛&#xff1f; 私域的定義 私域的界定&#xff1a;一個互聯網私有數據&#xff08;資產&#xff09;積蓄的載體。這個載體的數據權益私有&#xff0c;且具備用戶規則制定權…

Mysql 備份恢復 mysqldump與xtrabackup備份

1.1 備份的原因 備份是數據安全的最后一道防線&#xff0c;對于任何數據丟失的場景&#xff0c;備份雖然不一定能恢復百分之百的數據 (取決于備份周期)&#xff0c;但至少能將損失降到最低。衡量備份恢復有兩個重要的指標&#xff1a;恢復點目標(RPO) 和恢復時間目標(RTO)&…

數據庫常用命令(1)

DML 1.添加數據&#xff08;insert into&#xff09; insert into 表名 values (值1&#xff0c;值2....); 表示成功運行&#xff1a; 2.修改數據&#xff08;update&#xff09; update 表名 set 字段名1值1&#xff0c;字段名2值2.....【where條件】 3.刪除數據&#xff0…

元年科技數據智能研發部負責人張亞東受邀為第十三屆中國PMO大會演講嘉賓

全國PMO專業人士年度盛會 北京元年科技股份有限公司數據智能研發部負責人張亞東先生受邀為PMO評論主辦的2024第十三屆中國PMO大會演講嘉賓&#xff0c;演講議題為“大模型時代&#xff0c;AI創新型工具提升項目管理效率”。大會將于6月29-30日在北京舉辦&#xff0c;敬請關注&a…