深入理解設計模式之代理模式:原理、實現與應用

在軟件開發中,我們經常需要控制對某些對象的訪問——可能是為了延遲加載、添加額外功能或保護敏感資源。這正是代理模式大顯身手的地方。作為結構型設計模式的重要成員,代理模式在眾多知名框架和系統中扮演著關鍵角色。本文將全面剖析代理模式的方方面面,帶你領略這一模式的精妙之處。

一、代理模式概述

1.1 什么是代理模式

代理模式(Proxy Pattern)是一種結構型設計模式,它為其他對象提供一種代理以控制對這個對象的訪問。簡單來說,代理就是一個"替身",它代表另一個對象(即真實對象)執行某些操作,同時可以在訪問真實對象前后添加自己的邏輯。

1.2 代理模式的核心思想

代理模式的核心在于"控制訪問",它遵循了面向對象設計原則中的"單一職責原則"和"開閉原則":

  • 單一職責:真實對象只需關注核心業務邏輯,而訪問控制、日志記錄等職責交給代理

  • 開閉原則:可以在不修改真實對象代碼的情況下,通過代理擴展功能

1.3 生活中的代理類比

現實生活中代理的例子比比皆是:

  • 房屋中介:代理房東處理租房事宜

  • 明星經紀人:代理明星安排演出和商業活動

  • 信用卡:代理銀行賬戶進行支付

這些代理的共同特點是:它們都代表另一個實體執行操作,同時可以添加自己的處理邏輯(如中介收取傭金、經紀人篩選邀約等)。

二、代理模式的結構與實現

2.1 UML類圖

2.2 核心角色

  1. Subject(抽象主題)

    • 定義真實主題和代理主題的共同接口

    • 可以是接口或抽象類

  2. RealSubject(真實主題)

    • 實現真實業務邏輯的類

    • 是被代理的對象

  3. Proxy(代理)

    • 包含對真實主題的引用

    • 實現與真實主題相同的接口

    • 可以控制對真實主題的訪問

2.3 Java實現示例

// 抽象主題
interface Database {void query(String sql);
}// 真實主題
class RealDatabase implements Database {@Overridepublic void query(String sql) {System.out.println("執行查詢: " + sql);// 實際數據庫操作...}
}// 代理
class DatabaseProxy implements Database {private RealDatabase realDatabase;private List<String> blacklist = Arrays.asList("DELETE", "DROP", "TRUNCATE");@Overridepublic void query(String sql) {// 安全檢查if (containsBlacklistedKeywords(sql)) {throw new RuntimeException("查詢包含危險操作");}// 日志記錄System.out.println("[" + LocalDateTime.now() + "] 執行查詢: " + sql);// 延遲初始化if (realDatabase == null) {realDatabase = new RealDatabase();}// 執行實際查詢realDatabase.query(sql);// 后置處理System.out.println("查詢完成");}private boolean containsBlacklistedKeywords(String sql) {return blacklist.stream().anyMatch(sql::contains);}
}// 客戶端代碼
public class Client {public static void main(String[] args) {Database database = new DatabaseProxy();database.query("SELECT * FROM users");  // 正常執行database.query("DROP TABLE users");     // 拋出異常}
}

這個示例展示了一個數據庫查詢代理,它實現了:

  1. 安全檢查(保護代理)

  2. 日志記錄(智能引用代理)

  3. 延遲初始化(虛擬代理)

三、代理模式的類型

3.1 遠程代理(Remote Proxy)

特點:為位于不同地址空間的對象提供本地代表

應用場景

  • RPC(遠程過程調用)

  • Web服務客戶端

  • 分布式系統中的存根(Stub)

示例

// 遠程服務接口
interface WeatherService {String getWeather(String city);
}// 本地代理
class WeatherServiceProxy implements WeatherService {@Overridepublic String getWeather(String city) {// 通過網絡調用遠程服務return callRemoteWeatherService(city);}private String callRemoteWeatherService(String city) {// 實際網絡通信邏輯...return "Sunny";}
}

3.2 虛擬代理(Virtual Proxy)

特點:根據需要創建開銷很大的對象

應用場景

  • 大圖加載

  • 復雜對象初始化

  • 資源密集型操作

示例

class HighResolutionImage implements Image {public HighResolutionImage(String path) {loadImage(path); // 耗時操作}private void loadImage(String path) {// 加載大圖...}
}class ImageProxy implements Image {private String path;private HighResolutionImage realImage;public ImageProxy(String path) {this.path = path;}@Overridepublic void show() {if (realImage == null) {realImage = new HighResolutionImage(path); // 延遲加載}realImage.show();}
}

3.3 保護代理(Protection Proxy)

特點:控制對原始對象的訪問權限

應用場景

  • 權限控制

  • 敏感操作保護

  • 訪問限制

示例

interface BankAccount {void withdraw(double amount);double getBalance();
}class RealBankAccount implements BankAccount {private double balance;@Overridepublic void withdraw(double amount) {balance -= amount;}@Overridepublic double getBalance() {return balance;}
}class BankAccountProxy implements BankAccount {private RealBankAccount account;private User user;public BankAccountProxy(User user) {this.user = user;this.account = new RealBankAccount();}@Overridepublic void withdraw(double amount) {if (user.hasPermission("WITHDRAW")) {account.withdraw(amount);} else {throw new SecurityException("無取款權限");}}@Overridepublic double getBalance() {if (user.hasPermission("VIEW_BALANCE")) {return account.getBalance();} else {throw new SecurityException("無查看余額權限");}}
}

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

特點:在訪問對象時執行附加操作

應用場景

  • 引用計數

  • 對象池管理

  • 緩存機制

示例

class ExpensiveObject {void process() {System.out.println("處理中...");}
}class SmartProxy {private ExpensiveObject realObject;private int accessCount = 0;public void process() {if (realObject == null) {realObject = new ExpensiveObject();}accessCount++;System.out.println("訪問次數: " + accessCount);realObject.process();if (accessCount >= 5) {System.out.println("重置對象...");realObject = null;accessCount = 0;}}
}

四、代理模式的深入應用

4.1 Spring框架中的代理

Spring框架廣泛使用代理模式,主要體現在:

  1. AOP(面向切面編程)

    • Spring AOP使用JDK動態代理或CGLIB代理實現

    • 為業務組件添加事務管理、日志記錄等橫切關注點

  2. 事務管理

    • @Transactional注解背后的代理機制

    • 在方法調用前后管理事務邊界

  3. @Configuration類

    • 配置類的代理確保@Bean方法單例性

示例

@Service
public class UserService {@Transactionalpublic void createUser(User user) {// 數據庫操作}
}// Spring在運行時創建代理類
class UserServiceProxy extends UserService {private UserService target;private PlatformTransactionManager txManager;@Overridepublic void createUser(User user) {TransactionStatus status = txManager.getTransaction(new DefaultTransactionDefinition());try {target.createUser(user);txManager.commit(status);} catch (Exception e) {txManager.rollback(status);throw e;}}
}

4.2 MyBatis中的代理

MyBatis使用JDK動態代理實現Mapper接口:

public interface UserMapper {@Select("SELECT * FROM users WHERE id = #{id}")User getUserById(int id);
}// MyBatis在運行時生成代理實現
class MapperProxy implements InvocationHandler {private SqlSession sqlSession;@Overridepublic Object invoke(Object proxy, Method method, Object[] args) {// 解析注解中的SQLString sql = method.getAnnotation(Select.class).value();// 執行SQL并返回結果return sqlSession.selectOne(sql, args[0]);}
}

4.3 RPC框架中的代理

遠程方法調用(RPC)框架如Dubbo、gRPC都使用代理模式:

// 服務接口
public interface OrderService {Order getOrder(long id);
}// 客戶端代理
class OrderServiceProxy implements OrderService {private String serviceUrl;@Overridepublic Order getOrder(long id) {// 序列化參數byte[] request = serialize(id);// 網絡調用byte[] response = sendRequest(serviceUrl, request);// 反序列化結果return deserialize(response);}
}

五、代理模式的優缺點

5.1 優點

  1. 職責分離:代理對象處理非功能性需求(如安全、日志),真實對象專注業務邏輯

  2. 開閉原則:無需修改真實對象即可擴展功能

  3. 訪問控制:代理可以控制對真實對象的訪問

  4. 性能優化:虛擬代理可以實現延遲加載,提高系統響應速度

5.2 缺點

  1. 復雜度增加:引入代理層會增加系統復雜度

  2. 性能開銷:代理調用會帶來額外的處理時間

  3. 間接性:可能使調試變得困難,因為調用棧更深

六、代理模式與相關模式的比較

6.1 代理模式 vs 裝飾器模式

對比維度代理模式裝飾器模式
目的控制訪問增強功能
關系代理知道被代理對象的生命周期裝飾器與被裝飾對象獨立
關注點訪問機制(如延遲加載、權限控制)添加新行為

6.2 代理模式 vs 適配器模式

對比維度代理模式適配器模式
接口保持相同接口轉換不同接口
目的控制訪問解決接口不兼容問題
使用時機設計階段規劃后期集成時使用

七、實際應用建議

  1. 何時使用代理模式

    • 需要延遲初始化(虛擬代理)

    • 需要控制資源訪問(保護代理)

    • 需要添加橫切關注點(AOP)

    • 需要遠程調用(遠程代理)

  2. 實現選擇

    • 靜態代理:代理類在編譯時確定,適合簡單場景

    • 動態代理:運行時生成代理類,更靈活(JDK動態代理、CGLIB)

  3. 性能考慮

    • 對于頻繁調用的方法,注意代理帶來的性能開銷

    • 考慮使用輕量級代理或直接訪問

  4. 設計原則

    • 遵循"最少知識原則",代理不應暴露過多真實對象細節

    • 保持代理接口簡潔,避免成為"上帝對象"

結語

代理模式作為設計模式家族中的重要成員,其應用范圍從簡單的對象訪問控制到復雜的框架實現無處不在。理解并掌握代理模式,不僅能幫助我們設計出更加靈活、安全的系統,還能深入理解眾多流行框架的內部工作機制。無論是日常開發中的權限控制、日志記錄,還是分布式系統中的遠程調用,代理模式都展現出其強大的適應性和生命力。希望本文能為你打開代理模式的大門,助你在軟件設計之路上更進一步。

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

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

相關文章

VSCode - VSCode 快速跳轉標簽頁

VSCode 快速跳轉標簽頁 1、標簽頁列表快速跳轉 通過快捷鍵 Ctrl Tab 即可快速跳轉標簽頁 # 操作方式先按住 Ctrl 鍵&#xff0c;再按 Tab 鍵&#xff0c;此時&#xff0c;即可打開標簽頁列表&#xff08;保持 Ctrl 鍵一直按住&#xff09;然后&#xff0c;再按 Tab 鍵&#xf…

深入理解設計模式:享元模式(Flyweight Pattern)

在軟件開發中&#xff0c;我們經常會遇到需要創建大量相似對象的情況。如果每個對象都獨立存儲所有數據&#xff0c;將會消耗大量內存資源&#xff0c;導致系統性能下降。享元模式&#xff08;Flyweight Pattern&#xff09;正是為解決這一問題而生的經典設計模式。本文將深入探…

網絡大提速,RDMA,IB,iWrap

本章第一節介紹的存儲設備方面的創新解決了CPU訪問存儲設備的性能問題。但在實際的業務當中,數據的傳輸除了在節點內部的CPU與存儲設備間外,節點之間也存在數據傳輸的需求。本節我們就介紹在網絡傳輸方面是如何提速的。 在介紹新的網絡技術之前,我們看看傳統網絡是如何傳輸…

【C++】紅黑樹,“紅“與“黑”的較量

各位大佬好&#xff0c;我是落羽&#xff01;一個堅持不斷學習進步的大學生。 如果您覺得我的文章有所幫助&#xff0c;歡迎多多互三分享交流&#xff0c;一起學習進步&#xff01; 也歡迎關注我的blog主頁: 落羽的落羽 一、紅黑樹的概念與規則 紅黑樹是一種更加特殊的平衡二…

【愚公系列】《MIoT.VC》001-認識、安裝 MIoT.VC 軟件

??【行業認證權威頭銜】 ? 華為云天團核心成員:特約編輯/云享專家/開發者專家/產品云測專家 ? 開發者社區全滿貫:CSDN博客&商業化雙料專家/阿里云簽約作者/騰訊云內容共創官/掘金&亞馬遜&51CTO頂級博主 ? 技術生態共建先鋒:橫跨鴻蒙、云計算、AI等前沿領域…

git:tag標簽遠程管理

git tag v1&#xff1a;在當前所在分支創建標簽v1git tag -a v2 -m release version&#xff1a;創建一個帶有附注的標簽git tag -d v2&#xff1a;刪除本地標簽git tag&#xff1a;查看標簽git push origin 標簽1 標簽2……&#xff1a;把多個標簽推送到遠程git push origin -…

力扣 hot100 Day49

105. 從前序與中序遍歷序列構造二叉樹 給定兩個整數數組 preorder 和 inorder &#xff0c;其中 preorder 是二叉樹的先序遍歷&#xff0c; inorder 是同一棵樹的中序遍歷&#xff0c;請構造二叉樹并返回其根節點。 //抄的 class Solution { private:unordered_map<int, i…

jvm-sandbox-repeater 錄制和回放

https://github.com/alibaba/jvm-sandbox-repeater/blob/master/docs/user-guide-cn.md 快速錄制自己應用 step0 安裝sandbox和插件到應用服務器 curl -s https://github.com/alibaba/jvm-sandbox-repeater/releases/download/v1.0.0/install-repeater.sh | sh step1 修改repe…

【C++底層剖析】++a vs a++:到底誰是左值,誰是右值?

在 C 編程中&#xff0c;我們經常使用 a 和 a 來實現自增操作。乍一看它們只是“先加還是后加”的語法糖&#xff0c;但你真的理解它們的底層機制、返回值類型和左值右值屬性嗎&#xff1f;1. a 和 a 的基礎區別表達式名稱語義返回值類型左值 / 右值a前置自增先將 a 加 1&#…

【世紀龍科技】汽車故障診斷與排除仿真教學軟件讓課堂更高效安全

隨著汽車產業向智能化、電動化快速轉型&#xff0c;職業院校汽修專業的教學模式正面臨全新挑戰。傳統實車實訓存在成本高、風險大、場景單一等問題&#xff0c;而行業對人才的要求卻越來越高——既需要扎實的理論基礎&#xff0c;又必須具備熟練的故障診斷能力。如何在保證安全…

網絡基礎9:按流負載均衡實驗(等價路由)

實驗eNS拓撲圖&#xff1a;1. 網絡拓撲與 IP 配置AR5&#xff1a;GE0/0/0: 192.168.1.1/24&#xff08;連接 AR6&#xff09;GE0/0/1: 192.168.3.1/24&#xff08;連接 AR8&#xff09;Loopback0: 1.1.1.1/32&#xff08;源地址&#xff09;AR6&#xff1a;GE0/0/0: 192.168.1.…

4G模塊 A7680發送中文短信到手機

命令說明 基礎AT指令 ATi顯示產品的標志信息 ATCIMI查詢IMSI ATCICCID從SIM卡讀取ICCID ATCGSN查詢產品序列號 ATCPIN查詢卡狀態 ATCSQ查詢信號強度 ATCGATT查詢當前PS域狀態 ATCREG查詢GPRS注冊狀態 ATCEREG查詢4G注冊狀態 ATCGPADDR查詢PDP地址 ATCMGF選擇短信格式 ATCMGS發…

深度學習-線性神經網絡

文章目錄線性回歸基本概念隨機梯度下降矢量化加速正態分布和平方損失極大似然估計線性回歸實現從0開始**torch.no_grad()的兩種用途****為什么需要 l.sum().backward()&#xff1f;**調用現成庫softmax回歸圖像數據集從0開始實現softmax利用框架API實現課程學習自李牧老師B站的…

【王樹森推薦系統】推薦系統漲指標的方法04:多樣性

漲指標的方法有哪些&#xff1f; 改進召回模型&#xff0c;添加新的召回模型改進粗排和精排模型提升召回&#xff0c;粗排&#xff0c;精排的多樣性特殊對待新用戶嗎&#xff0c;低活用戶等特殊人群利用關注&#xff0c;轉發&#xff0c;評論這三種交互行為 排序的多樣性 精排多…

1. Spring AI概述

一、前言 Spring AI 是由 Spring 團隊推出的開源項目&#xff0c;旨在為 Java 開發者提供簡潔、一致的 Spring 風格開發體驗&#xff0c;用于構建基于生成式人工智能&#xff08;GenAI&#xff09;和大型語言模型&#xff08;LLM&#xff09;的應用程序。它通過標準化抽象層簡…

[每日隨題10] DP - 重鏈剖分 - 狀壓DP

整體概述 難度&#xff1a;1600 →\rightarrow→ 2200 →\rightarrow→ 2600 P6005 [USACO20JAN] Time is Mooney G 標簽&#xff1a;DP 前置知識&#xff1a;鏈式前向星 難度&#xff1a;綠 1600 題目描述&#xff1a; 輸入格式&#xff1a; 輸出格式&#xff1a; 樣例輸…

【Ubuntu22.04】repo安裝方法

背景 repo是Google開發的用于基于git管理Android版本庫的一個工具&#xff0c;管理多個Git倉庫的工具&#xff0c;它可以幫助您在一個代碼庫中管理多個Git倉庫的代碼。其在鴻蒙操作系統中大量使用。下面我們就介紹repo在wsl中的安裝部署。 安裝方法 使用中國科技大學資源 腳本i…

Vue3的definePros和defineEmits

在 Vue 3 中&#xff0c;defineProps 和 defineEmits 是組合式 API 中用于定義組件的 props 和 事件 的方法&#xff0c;提供了一種更簡潔和明確的方式來管理組件的輸入和輸出。它們屬于 Composition API 的一部分&#xff0c;在 Vue 2 中通常使用 props 和 $emit 來實現。1. d…

【華為機試】122. 買賣股票的最佳時機 II

文章目錄122. 買賣股票的最佳時機 II描述示例 1示例 2示例 3提示解題思路核心觀察關鍵洞察算法實現方法1&#xff1a;貪心算法&#xff08;推薦&#xff09;方法2&#xff1a;動態規劃方法3&#xff1a;動態規劃&#xff08;空間優化&#xff09;方法4&#xff1a;波峰波谷法算…

Spring MVC @RequestParam注解全解析

RequestParam 注解詳解 RequestParam 是 Spring MVC 中最常用的注解之一&#xff0c;用于從 HTTP 請求中提取查詢參數&#xff08;Query String&#xff09;或表單數據。它主要處理 application/x-www-form-urlencoded 類型的請求&#xff08;如 GET 請求或 POST 表單提交&…