代理模式筆記

代理模式

  • 代理模式
  • 代理模式的應用場景
  • 先理解什么是代理,再理解動靜態
    • 舉例
    • 舉例所用代碼
  • 動靜態的區別
    • 靜態代理
    • 動態代理
  • 動態代理的優點
  • 代理模式與裝飾者模式的區別

代理模式

代理模式在設計模式中是7種結構型模式中的一種,而代理模式有分動態代理,靜態代理,一般來說,動態代理更加常用一些。

代理模式的應用場景

這些應用場景除了日志記錄,我也沒有熟悉的,剛接觸代理模式的可以直接跳過

遠程代理(Remote Proxy):

當對象存在于不同的地址空間,例如在網絡中的不同服務器上時,可以使用代理模式實現遠程代理。代理對象充當本地對象的代表,隱藏了遠程對象的實際細節,使得客戶端可以像調用本地對象一樣調用遠程對象。

虛擬代理(Virtual Proxy):

虛擬代理用于按需創建昂貴或復雜的對象,以提高系統性能。代理對象在真正需要執行操作時才會實例化真實對象,而在其他情況下,它充當一個占位符。

保護代理(Protection Proxy):

保護代理用于控制對對象的訪問權限。代理對象可以根據訪問者的身份控制其對真實對象的訪問,例如,檢查用戶是否具有足夠的權限來執行某個操作。

緩存代理(Cache Proxy):

緩存代理用于緩存一些開銷較大的操作的結果,以避免重復計算。代理對象在執行真實對象的操作之前檢查是否已經有相應的結果緩存,如果有則直接返回緩存的結果。

日志記錄代理(Logging Proxy):

日志記錄代理用于在調用真實對象的操作前后記錄相關日志信息,例如,記錄方法的執行時間、參數、返回值等,以便進行調試或性能分析。

智能引用代理(Smart Reference Proxy):

智能引用代理用于在對象被引用時執行一些額外的操作,例如,對對象的引用計數進行管理,當引用計數為零時釋放對象資源。

延遲加載代理(Lazy Loading Proxy):

延遲加載代理用于延遲加載對象的實例,即在真正需要使用對象時才進行加載。這可以提高系統的啟動性能,避免在啟動時加載不必要的資源。

先理解什么是代理,再理解動靜態

代理模式的靈魂就是在不直接訪問某個對象的情況下,通過代理對象來間接訪問并控制對該對象的訪問。(在這個間接訪問的過程中代理對象通常會在執行代理對象里的操作先后時間段里執行一些被代理對象里沒有的操作

我們先搞清楚代理模式有幾個角色,再來舉例

真實對象(被代理對象): 被間接訪問的對象

代理對象: 代理對象將間接訪問真實對象,并且代理可以幫助你做一些額外的事情,比如檢查你的權限、記錄你的請求、或者緩存結果。

抽象類或接口(一般是接口): 這是代理對象和真實對象都要實現的接口(建立一個聯系),這樣代理才可以替代真實對象。一般這個接口里的抽象方法是代理對象訪問被代理對象的關鍵。

舉例

一天,四年級三班同學舉行班級里的數學期中考試,考試時間結束后,由小明同學(數學學習委員)將試卷收好送給數學老師,數學老師批改完試卷后,小明又會將試卷拿回,并將班級數學成績統計出來。
在這里插入圖片描述

在了解完代理模式的三個角色后,我們嘗試把上面的例子進行角色分析

在這里插入圖片描述

真實對象(被代理對象):數學老師

你可以理解成數學老師才是期中數學考試出成績的關鍵,但在同學們知曉成績時,他并沒有出面。

代理對象:小明——數學學習委員

雖然數學老師才是數學考試出成績的關鍵,但是出成績時,他才是在同學們露面的人,并且他還額外進行了統計成績的操作(類似程序的日志記錄)。

抽象類或者接口:期中考試

期中考試是聯系數學老師和數學學習委員的一個關鍵,當然你也可以用其他的關鍵詞來描述這個接口,但是批改試卷是這個例子的關鍵,如果不用學生可以自己批改試卷,那么老師就不用出現了,就不用訪問數學老師這個對象了,所以接口里必須要有批改試卷這個方法。

舉例所用代碼

這里我們先創建抽象接口

public interface IMidterm_Examination {//批改試卷的抽象方法void markPapers();
}

再創建具體的數學老師類,也就是真實對象類或者說被代理類

// 在實現抽象接口的前提下創建真實對象
public class MathTeacher implements IMidterm_Examination {//基本屬性private int age=32;private String name="李四";private String job="數學老師";@Overridepublic void markPapers() {System.out.println("數學老師正在改試卷");}
}

在創建代理對象,創建代理對象前需先寫出代理對象類

public class MathMonitor  implements IMidterm_Examination{private int age=16;private String name="小明";private String job="數學學習委員";private IMidterm_Examination target;//代理目標,即被代理對象,接口是為了更加靈活通用public MathMonitor(IMidterm_Examination target) {this.target = target;//在構造代理對象時,將被代理對象傳入}void sendPaper(){System.out.println("小明同學將試卷送給老師");}//小明額外的統計成績方法void  countScores(){System.out.println("小明同學正在統計成績");}@Overridepublic void markPapers() {//重寫抽象接口里的方法//顯示小明同學將試卷送給數學老師this.sendPaper();//老師來修改試卷target.markPapers();//老師批改完試卷后,小明統計成績this.countScores();System.out.println("期中考試流程結束");}
}

測試主函數

public class Main {public static void main(String[] args) {//通過接口方式創建被代理對象,IMidterm_Examination mathTeacher=new MathTeacher();//再通過接口創建代理對象IMidterm_Examination mathMonitor=new MathMonitor(mathTeacher);//通過代理對象間接訪問數學老師這個對象mathMonitor.markPapers();}
}

運行結果
在這里插入圖片描述

在看完上面的例子后,我們知道代理模式中的代理就是一個類似中介的效果,就像找工作一樣,小王本來想進某家電子廠的,但是需要交中介費才能進入這個廠,但是這個中介還會包你不滿意該電子廠環境拒絕進廠來回的路費一樣。

動靜態的區別

靜態代理

靜態代理在上面的舉例代碼中已經體現出了,它有以下特點:
也許你在讀完這些特點你還是會不太理解,所以你可以在看完動態代理之后再來回顧靜態代理,才能感受到這些特點。

編譯時確定:

在編譯期間,代理類的代碼就已經確定。這意味著代理類的結構在編譯時就已經固定,不會在運行時改變。

代理類固定:

靜態代理需要為每個被代理的類創建一個代理類。這意味著如果要代理多個類,就需要為每個類編寫一個對應的代理類。

低靈活性:

由于代理類在編譯時已經確定,因此靜態代理的靈活性相對較低。如果需要修改代理類的行為,通常需要修改代理類的源代碼,并重新編譯。

性能較高:

靜態代理的方法調用在編譯期間就已經確定,因此在運行時的性能通常比動態代理高。代理對象直接調用被代理對象的方法,不需要進行額外的方法查找或調用。

動態代理

我們將之前那個例子稍微拓展一下,四年級三班的同學上午進行了數學期中考試后,下午又進行了英語期中考試,可是四年級三班的英語課代表生病請假了,所以英語老師也麻煩小明同學(數學學習委員)來收試卷并且把試卷送給老師,最后再把試卷送到班級里。

你先別急著否認這個靜態代理做不到,靜態代理同樣能完成這件事情,我們再試著用靜態代理來完成這件事,
我們回顧一下之前小明同學的代碼:
我們的代理對象的構造函數中的參數是接口,那么理論上只要英語老師也實現這個接口,他也能傳進去,那就試試。

public class MathMonitor  implements IMidterm_Examination{private int age=16;private String name="小明";private String job="數學學習委員";private IMidterm_Examination target;//代理目標,即被代理對象,接口是為了更加靈活通用public MathMonitor(IMidterm_Examination target) {this.target = target;//在構造代理對象時,將被代理對象傳入}

我們定義的接口不變

public interface IMidterm_Examination {//批改試卷的抽象方法void markPapers();
}

再來創建一個英語老師的被代理對象的類實現期中考試接口:

public class EnglishTeacher implements IMidterm_Examination{private int age=24;private String name="王雪";private String job="英語老師";@Overridepublic void markPapers() {System.out.println("英語老師正在批改英語試卷");}
}

只需要在測試主函數中傳入英語老師這個被代理對象,就行了

public static void main(String[] args) {//通過接口創建被代理對象,IMidterm_Examination mathTeacher=new MathTeacher();//再通過接口創建代理對象IMidterm_Examination mathMonitor=new MathMonitor(mathTeacher);//通過代理對象間接訪問數學老師這個對象mathMonitor.markPapers();//通過創建被代理對象,IMidterm_Examination englishTeacher=new EnglishTeacher();mathMonitor=new MathMonitor(englishTeacher);//通過代理對象間接訪問英語老師這個對象mathMonitor.markPapers();}

運行結果:
在這里插入圖片描述
其實這么來說,靜態代理也有點“動”的意思,一個代理對象也能完成多個被代理對象的代理。
但是在真正的動態代理面前,它還差遠了。

前提是被代理類與代理類實現了相同的接口


動態代理的最大特色是代理類不需要實現與被代理類相同的接口就能實現代理,也就是說代理類在java中,動態代理一般有JDK接口和CGLib兩種方式進行實現,這里只介紹JDK接口方法。

我們動態代理來完成上面的例子

需要大改代碼的就是代理類,也就是小明這個數學學習委員的代碼,
代理類不在需要我們自定義的期中考試的接口
但它需要實現官方提供的InvocationHandler接口

public class MathMonitor  implements InvocationHandler {private int age=16;private String name="小明";private String job="數學學習委員";private Object target;public MathMonitor(Object target) {this.target = target;}void sendPaper(){System.out.println("小明同學將試卷送給老師");}//小明額外的統計成績方法void  countScores(){System.out.println("小明同學正在統計成績");}/** @param* Object proxy 傳入代理對象* Method method 傳入需要執行的方法* Object[] args  方法需要的參數數組* @return 返回一個Object類型的對象**/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//在間接訪問對象前可做的事情this.sendPaper();Object result = method.invoke(target, args);//在間接訪問對象后可做的事情this.countScores();return result;//在Java的反射API中,Method 類的 invoke 方法用于動態地調用一個方法。在你提供的 invoke 方法中,//這個 method.invoke(target, args) 調用實際上是在執行被代理對象(target)上的方法,// 并且傳遞了參數(args)。//method.invoke 的返回值就是該方法調用的結果。換句話說,它返回了被代理對象上被調用的方法的返回值。//如果方法類型是void,那么result值是null值}
}

測試主函數里的代碼也有點變化

//創建兩個被代理對象IMidterm_Examination mathTeacher =new MathTeacher();IMidterm_Examination englishTeacher=new EnglishTeacher();//先代理數學老師MathMonitor mathMonitor=new MathMonitor(mathTeacher);IMidterm_Examination proxy = (IMidterm_Examination) Proxy.newProxyInstance(Main.class.getClassLoader(),//獲取類加載器new Class[]{IMidterm_Examination.class},mathMonitor);proxy.markPapers();mathMonitor=new MathMonitor(englishTeacher);proxy = (IMidterm_Examination) Proxy.newProxyInstance(Main.class.getClassLoader(),//獲取類加載器new Class[]{IMidterm_Examination.class},mathMonitor);proxy.markPapers();}
}

Proxy.newProxyInstance 是 Java 動態代理的核心方法,用于創建一個新的代理實例。這個方法需要三個參數:

類加載器(ClassLoader):Main.class.getClassLoader()

類加載器用于加載代理類。在 Java 中,每個類都有一個類加載器,它負責加載類的字節碼文件。在動態代理中,代理類是在運行時生成的,因此需要一個類加載器來加載這個新生成的類。在這個例子中,使用 Main.class.getClassLoader() 獲取 Main 類的類加載器來加載代理類。

代理接口數組(Class<?>[] interfaces):new Class[]{IMidterm_Examination.class}

這個參數指定了代理實例需要實現的接口列表。代理實例將實現這些接口中定義的所有方法。當代理實例上的這些方法被調用時,它們將被轉發到 InvocationHandler 的 invoke 方法。在這個例子中,代理實例將實現 IMidterm_Examination 接口。

調用處理器(InvocationHandler):mathMonitor

InvocationHandler 是一個接口,它里面定義了一個 invoke 方法,用于處理代理實例上的方法調用。即傳入代理對象

動態代理的優點

動態代理是一種在運行時動態創建代理對象的機制,它允許你在調用實際對象之前或之后執行額外的操作。以下是動態代理的一些優點:

靈活性:

動態代理允許你在運行時創建代理對象,因此你可以根據需要動態地選擇要代理的對象,而無需在編譯時確定。這使得代碼更加靈活和可擴展。

減少重復代碼:

通過使用動態代理,你可以將一些通用的代碼邏輯(例如日志記錄、性能監控、事務管理等)從業務邏輯中分離出來,并將其放入代理對象中。這樣可以減少重復代碼,提高代碼的可維護性。

簡化代碼結構:

動態代理可以幫助你將關注點分離(Separation of Concerns),將橫切關注點(cross-cutting concerns)從核心業務邏輯中解耦。這樣可以使得代碼結構更加清晰,易于理解和維護。

提高代碼復用性:

通過將通用的功能封裝在代理對象中,可以使得這些功能在多個地方被重復使用,從而提高了代碼的復用性。

動態性:

由于動態代理是在運行時創建的,因此你可以根據需要動態地添加、修改或刪除代理對象的行為,而無需修改原始對象或重新編譯代碼。這使得系統更加靈活和動態。

代理模式與裝飾者模式的區別

在學習中,我很容易把裝飾器模式和代理模式混淆,老師說在現實開發中,確實是兩個都會混著用的,但是它們還是有一點小區別的。
意圖不同:

代理模式的主要目的是控制對對象的訪問。代理對象通常作為原始對象的接口,允許你在不直接訪問原始對象的情況下控制對其的訪問。
裝飾者模式的主要目的是為對象動態添加新的功能。裝飾者模式允許你通過將對象包裝在一個或多個裝飾者中,來動態地添加或修改對象的行為,而不需要改變其接口。

關注點不同:

代理模式的關注點在于控制對對象的訪問,通常涉及在訪問原始對象之前或之后執行額外的操作,如權限驗證、延遲加載、緩存等。
裝飾者模式的關注點在于動態地為對象添加新的行為,通常涉及在對象的行為上面添加修飾,如增加新的功能、改變行為等。

組合方式不同:

代理模式通常是一對一的關系,即每個代理對象只代理一個真實對象,并通過這個代理對象來控制對真實對象的訪問。
裝飾者模式則可以是多對一的關系,即一個對象可以被多個裝飾者對象裝飾,每個裝飾者對象可以在不影響其他裝飾者的情況下獨立地添加新的行為。

生命周期不同:

代理模式的生命周期通常與被代理對象相關聯,代理對象的創建和銷毀由被代理對象的創建和銷毀來管理。
裝飾者模式的生命周期則通常是短暫的,裝飾者對象通常是在運行時動態添加到被裝飾對象上,可以根據需要隨時添加或刪除。

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

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

相關文章

rabbitmq知識梳理

一.WorkQueues模型 Work queues&#xff0c;任務模型。簡單來說就是讓多個消費者綁定到一個隊列&#xff0c;共同消費隊列中的消息。 當消息處理比較耗時的時候&#xff0c;可能生產消息的速度會遠遠大于消息的消費速度。長此以往&#xff0c;消息就會堆積越來越多&#xff0c…

四、矩陣的分類

目錄 1、相等矩陣 2、同形矩陣 3、方陣&#xff1a; 4、負矩陣、上三角矩陣、下三角矩陣&#xff1a; 5、對角矩陣&#xff1a;是方陣 ?編輯7、單位矩陣&#xff1a;常常用 E或I 來表示。它是一個方陣 8、零矩陣&#xff1a; 9、對稱矩陣&#xff1a;方陣 1、相等矩陣 …

openEuler安裝MySQL客戶端、openEuler安裝MySQL-client、openEuler部署MySQL-client

MySQL客戶端下載鏈接&#xff1a;https://downloads.mysql.com/archives/community/ mysql-community-client-5.7.30-1.el7.x86_64.rpm mysql-community-common-5.7.30-1.el7.x86_64.rpm mysql-community-libs-5.7.30-1.el7.x86_64.rpm 3個必選 8.0.22以上的版本是4個&…

HDFS中常用的Shell命令 全面且詳細

HDFS中常用的Shell命令目錄 一、ls命令 二、mkdir 命令 三、put命令 四、get命令 五、mv命令 六、rm命令 七、cp命令 八、cat命令 前言 安裝好hadoop環境之后&#xff0c;可以執行hdfs相關的shell命令對hdfs文件系統進行操作&#xff0c;比如文件的創建、刪除、修改文…

【FPGA】VHDL:小型出勤系統設計

附源代碼&#xff0c;一定能實現&#xff01; 目錄 EDA設計練習題&#xff1a; 實驗要求如下&#xff1a; 思路分析&#xff1a; 代碼 99進制計數器 碼轉換 頂層文件 特別注意 測試 編譯通過 結果展示 RTL視圖 技術映射視圖 軟件&#xff1a;Quartus II 13.0 (64…

BERT學習筆記

論文&#xff1a;《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》&#xff0c;2019 代碼&#xff1a;[tensorflow]&#xff0c;[pytorch] 來源&#xff1a;李沐精度BERT 0、摘要 與之前模型的區別&#xff1a; GPT考慮的是一個單向…

公司中常用的系統有哪些--制造業篇

摘要 本系列博客主要介紹不同行業中使用的常見系統&#xff0c;本文介紹在制造業或是智能制造方向的常見系統。 智能制造發展史 1973年美國約瑟夫哈林頓&#xff08;Joseph Harrington&#xff09;博士在《Computer Integrated Manufacturing》一書中首次提出 CIM&#xff08…

C# 本地方法和lambda實現

概念&#xff1a; 本地函數是一種嵌套在另一成員中的類型的方法。 僅能從其包含成員中調用它們。 下面是本地方法最簡單的一個demo: public static int Show(){int c NewMethod(); return c;static int NewMethod(){#region 測試int a 3;int b 9;int c a b;#endregionre…

python opencv實現車牌識別

目錄 一:實現步驟: 二:實現車牌檢測 一:實現步驟: 使用Python和OpenCV實現車牌識別的步驟大致可以分為以下兩部分: 車牌檢測: 讀取需要進行車牌識別的圖片。 對圖像進行灰度化處理,可能還包括高斯模糊和灰度拉伸。 進行開運算,消除圖像中的噪聲。 將灰度拉伸后的圖…

培養納稅籌劃思維方式,企業稅務籌劃實務操作

一、教程描述 本套稅務籌劃教程&#xff0c;大小447.87M&#xff0c;共有6個文件。 二、教程目錄 前言.mp4 培養納稅籌劃思維方式.mp4 增值稅的稅務籌劃.mp4 企業所得稅的稅務籌劃.mp4 個人所得稅的稅務籌劃.mp4 企業稅務籌劃實務操作&#xff08;課件&#xff09;.pdf…

MDST150-16-ASEMI三相可控整流模塊MDST150-16

編輯&#xff1a;ll MDST150-16-ASEMI三相可控整流模塊MDST150-16 型號&#xff1a;MDST150-16 品牌&#xff1a;ASEMI 正向電流&#xff08;Id&#xff09;&#xff1a;150A 反向耐壓&#xff08;VRRM&#xff09;&#xff1a;1600V 正向浪涌電流&#xff1a;1200A 正…

大數據揭秘:Hadoop短視頻流量分析實戰

??計算機編程指導師 ??個人介紹&#xff1a;自己非常喜歡研究技術問題&#xff01;專業做Java、Python、微信小程序、安卓、大數據、爬蟲、Golang、大屏等實戰項目。 ??實戰項目&#xff1a;有源碼或者技術上的問題歡迎在評論區一起討論交流&#xff01; ?? Java實戰 |…

PyTorch概述(一)---圖像變換和增強

TorchVision torchvision 包由流行的數據集、模型架構以及用于計算機視覺的通用圖片轉換工具組成 TorchVision 包構成 圖像變換和增強TVTensors模型和預訓練的權重數據集工具操作圖像和視頻的編/解碼用于模型檢測的特征提取 圖像變換和增強 torchvision支持通用的計算機視…

基于springboot+vue的車輛管理系統(前后端分離)

博主主頁&#xff1a;貓頭鷹源碼 博主簡介&#xff1a;Java領域優質創作者、CSDN博客專家、阿里云專家博主、公司架構師、全網粉絲5萬、專注Java技術領域和畢業設計項目實戰&#xff0c;歡迎高校老師\講師\同行交流合作 ?主要內容&#xff1a;畢業設計(Javaweb項目|小程序|Pyt…

獲取淘寶商品詳情API、商品主圖、圖片搜索api

獲取淘寶詳情API的方式有以下幾種&#xff1a; 使用淘寶開放平臺提供的接口&#xff1a;淘寶開放平臺提供了多個API接口&#xff0c;讓開發者可以通過接口獲取商品詳情信息。你可以到淘寶開放平臺官網申請開發者賬號&#xff0c;并查看相關接口文檔&#xff0c;了解如何使用接…

C語言翻譯環境:預編譯+編譯+匯編+鏈接詳解

目錄 翻譯環境和運行環境 翻譯環境 預處理&#xff08;預編譯&#xff09; 編譯 詞法分析 語法分析 語義分析 匯編 鏈接 運行環境 ?翻譯環境和運行環境 在ANSI C的任何?種實現中&#xff0c;存在兩個不同的環境。 第1種是翻譯環境&#xff0c;在這個環境中源代碼被…

H橋逆變方式介紹(單極性)

H橋逆變電路實現的就是一個從DC——AC的過程 這個電路有兩個時序&#xff0c;Q6Q4是一個導通時序&#xff0c;Q5Q7是一個導通時序 左邊兩個是高頻20KHZ的、互補的sPWM波&#xff0c;右邊是低頻的50HZ的PWM波 三角波一般叫載波&#xff0c;正弦波叫調制波&#xff08;單片機內…

Hbase和Clickhouse對比簡單總結

Hbase和Clickhouse是兩種不同的數據庫系統&#xff0c;它們各自適用于不同的場景。以下是兩者之間的對比&#xff1a; 數據模型&#xff1a; HBase 是一種基于列的存儲系統&#xff0c;它適合處理大規模的數據集&#xff0c;特別是那些需要快速隨機訪問的場景。ClickHouse 則是…

對“絕地求生“源碼調節

調整了一些界面和降低游戲難度 #include "c.h" using namespace std; ExMessage msg; struct ME {int x 100; int y 100; int life 1000; int knapsack[18]; int Maximum_ammunition_capacity 0;int Ammunition_capacity 0; int primary_weapon 0; int moveme…

springboot/ssm倉庫管理系統Java貨物出入庫管理系統wms系統web

springboot/ssm倉庫管理系統Java貨物出入庫管理系統wms系統web 基于springboot(可改ssm)vue項目 開發語言&#xff1a;Java 框架&#xff1a;springboot/可改ssm vue JDK版本&#xff1a;JDK1.8&#xff08;或11&#xff09; 服務器&#xff1a;tomcat 數據庫&#xff1a…