Java设计模式 十三 代理模式 (Proxy Pattern)

server/2025/1/24 5:43:57/

代理模式 (Proxy Pattern)

代理模式是一种结构型设计模式,它为其他对象提供一种代理(或占位符)以控制对该对象的访问。通过代理模式,我们可以在不修改目标对象的情况下,控制对其的访问,添加额外的功能,比如懒加载、权限检查、日志记录等。

代理模式主要通过为目标对象创建一个代理对象来代替真实对象,代理对象可以控制对目标对象的访问,并可以在访问时提供附加功能。


1. 代理模式的组成

代理模式通常包括以下角色:

  • Subject(主题角色): 定义了真实对象和代理对象的公共接口。真实对象和代理对象都需要实现这个接口。
  • RealSubject(真实主题角色): 真实对象,定义了实际的业务操作逻辑。客户端可以通过代理对象来访问真实主题。
  • Proxy(代理角色): 代理对象,控制对真实对象的访问。代理对象可能会执行一些额外的操作(如权限检查、日志记录等),然后再委托给真实对象处理。
  • Client(客户端): 客户端通过代理对象来访问目标对象,而不是直接访问真实对象。

2. 代理模式的优点

  1. 透明化访问: 客户端无需知道自己是在访问代理对象还是实际对象,代理对象通过与实际对象相同的接口来提供服务,提供了透明的访问方式。
  2. 增强功能: 通过代理对象,能够在不改变目标对象的情况下,给目标对象添加额外的功能(如延迟加载、缓存、权限控制等)。
  3. 延迟加载: 可以在代理对象中实现延迟加载,直到真正需要时才初始化目标对象。
  4. 控制访问: 可以在代理对象中控制对真实对象的访问,比如添加安全检查、日志记录等功能。

3. 代理模式的缺点

  1. 性能开销: 代理模式可能会引入一些额外的性能开销,特别是代理对象需要进行额外的逻辑处理时,可能会影响性能。
  2. 增加复杂性: 代理模式会增加系统的复杂性,因为它引入了代理对象并且需要协调代理和目标对象之间的关系。

4. 代理模式的实现

场景示例:银行账户的访问控制

假设我们有一个银行账户类,用户可以通过该类来进行资金的存取操作。为了安全起见,银行希望对这些操作进行日志记录和权限控制。因此,我们可以使用代理模式来实现:

  1. 客户端直接操作代理对象。
  2. 代理对象在执行操作前进行权限检查、日志记录等,然后调用真实的银行账户操作。

1) 定义主题接口

首先定义一个接口,表示银行账户的基本操作。

java">// 主题接口
public interface BankAccount {void deposit(double amount);void withdraw(double amount);double getBalance();
}

2) 实现真实对象

真实对象实现了 BankAccount 接口,并提供具体的业务操作。

java">// 真实对象
public class RealBankAccount implements BankAccount {private double balance;public RealBankAccount(double initialBalance) {this.balance = initialBalance;}@Overridepublic void deposit(double amount) {balance += amount;System.out.println("Deposited " + amount + ", New balance: " + balance);}@Overridepublic void withdraw(double amount) {if (amount <= balance) {balance -= amount;System.out.println("Withdrew " + amount + ", New balance: " + balance);} else {System.out.println("Insufficient funds!");}}@Overridepublic double getBalance() {return balance;}
}

3) 实现代理对象

代理对象实现了 BankAccount 接口,并在方法调用时添加额外的功能(如权限控制和日志记录)。

java">// 代理对象
public class BankAccountProxy implements BankAccount {private RealBankAccount realBankAccount;public BankAccountProxy(double initialBalance) {realBankAccount = new RealBankAccount(initialBalance);}@Overridepublic void deposit(double amount) {logAction("deposit", amount);realBankAccount.deposit(amount);}@Overridepublic void withdraw(double amount) {logAction("withdraw", amount);realBankAccount.withdraw(amount);}@Overridepublic double getBalance() {return realBankAccount.getBalance();}private void logAction(String action, double amount) {System.out.println("Logging: Action = " + action + ", Amount = " + amount);}
}

4) 客户端代码

客户端通过代理对象来进行存款和取款操作,而不需要直接与真实对象交互。

java">public class Client {public static void main(String[] args) {BankAccount account = new BankAccountProxy(1000);// 客户端通过代理对象进行操作account.deposit(500);account.withdraw(200);account.withdraw(1000);System.out.println("Final balance: " + account.getBalance());}
}

运行结果:
Logging: Action = deposit, Amount = 500.0
Deposited 500.0, New balance: 1500.0
Logging: Action = withdraw, Amount = 200.0
Withdrew 200.0, New balance: 1300.0
Logging: Action = withdraw, Amount = 1000.0
Withdrew 1000.0, New balance: 300.0
Final balance: 300.0

在这个例子中,代理对象 BankAccountProxy 在调用真实银行账户的操作之前进行了日志记录,增加了访问控制的功能,而客户端并不需要知道这些细节。


5. 代理模式的应用场景

  1. 远程代理: 当一个对象位于不同的地址空间时,使用代理对象来控制对远程对象的访问(例如,远程方法调用(RMI))。
  2. 虚拟代理: 延迟加载资源或对象,直到真正需要时才创建。例如,图像的加载和显示通常使用虚拟代理模式来避免不必要的资源加载。
  3. 保护代理: 控制对真实对象的访问权限,比如验证用户的身份或权限。
  4. 智能代理: 代理对象可以添加一些额外的功能,比如引用计数、缓存、内存管理等。
  5. 缓存代理: 在代理中实现缓存功能,减少真实对象的重复访问。

6. 代理模式与其他模式的比较

设计模式主要用途代理模式的区别
装饰模式动态地给对象添加额外的功能。装饰模式通常是给对象添加行为,而代理模式是控制对对象的访问。
适配器模式将接口转化为客户端期望的接口,使得两个不兼容的接口可以协同工作。适配器模式用于接口适配,而代理模式用于控制访问。
单例模式确保类只有一个实例并提供全局访问点。单例模式通常是限制实例数量,而代理模式是控制对对象的访问。

7. 总结

代理模式是一种非常实用的设计模式,它通过引入代理对象来控制对真实对象的访问,从而为客户端提供附加的功能,比如权限控制、延迟加载、日志记录等。代理模式可以透明地为客户端提供真实对象的功能,同时避免客户端直接依赖目标对象,从而使得系统更具灵活性和可扩展性。

在实际应用中,代理模式可以应用于许多场景,如远程代理、虚拟代理、保护代理和智能代理等。通过使用代理模式,能够在不改变目标对象的情况下,为目标对象添加额外的功能,同时有效地控制对象的访问。


http://www.ppmy.cn/server/160956.html

相关文章

基于STM32的智能门锁安防系统(开源)

目录 项目演示 项目概述 硬件组成&#xff1a; 功能实现 1. 开锁模式 1.1 按键密码开锁 1.2 门禁卡开锁 1.3 指纹开锁 2. 功能备注 3. 硬件模块工作流程 3.1 步进电机控制 3.2 蜂鸣器提示 3.3 OLED显示 3.4 指纹与卡片管理 项目源代码分析 1. 主程序流程 (main…

iptables和ipvs差异

iptables和ipvs都是Linux内核中用于网络流量管理的工具&#xff0c;它们在实现方式、功能、性能以及使用场景上存在一些显著的差异。以下是对两者的详细比较&#xff1a; 一、实现方式 iptables&#xff1a; 基于Netfilter框架。使用链表&#xff08;chain&#xff09;和规则&…

大华相机DH-IPC-HFW3237M支持的ONVIF协议

使用libONVIF C库。 先发现相机。 配置 lib目录 包含 编译提示缺的文件&#xff0c;到libonvif里面拷贝过来。 改UDP端口 代码 使用msvc 2022的向导生成空项目&#xff0c;从项目的main示例拷贝过来。 CameraOnvif.h #pragma once#include <QObject> #include &l…

连接 OpenAI 模型:基础操作

在这一部分中&#xff0c;我们将介绍如何连接 OpenAI 模型&#xff0c;设置 API 密钥&#xff0c;并使用 Spring AI 的 ChatClient 与 OpenAI 模型进行简单的对话。Spring AI 为集成 OpenAI 模型提供了方便的工具&#xff0c;使得开发者能够更轻松地与 GPT 系列模型进行交互。 …

Windows 下本地 Docker RAGFlow 部署指南

Windows 下本地 Docker RAGFlow 部署指南 环境要求部署步骤1. 克隆代码仓库2. 配置 Docker 镜像加速(可选)3. 修改端口配置(可选)4. 启动服务5. 验证服务状态6. 访问服务7. 登录系统8. 配置模型8.1 使用 Ollama 本地模型8.2 使用在线 API 服务9. 开始使用10. 常见问题处理端…

WPF MVVM 模式如何监听IsVisibleChanged 事件

原本以为这是一个很简单的问题&#xff0c;但是我却走了不少的弯路。记录下来自省。 我使用的是库System.Windows.Interactivity.dll&#xff0c;首先在xaml 中使用了EventTrrigger <!-- 当 IsVisibleChanged 事件触发时&#xff0c;执行绑定的命令 --> <!--<…

代码随想录 栈与队列 test 7

347. 前 K 个高频元素 - 力扣&#xff08;LeetCode&#xff09; 首先想到哈希&#xff0c;用key来存元素&#xff0c;value来存出现次数&#xff0c;最后进行排序&#xff0c;时间复杂度约为o(nlogn)。由于只需求前k个&#xff0c;因此可以进行优化&#xff0c;利用堆来维护这…

npm install 报错:Command failed: git checkout 2.2.0-c

[TOC](npm install 报错&#xff1a;Command failed: git checkout 2.2.0-c) npm install 报错&#xff1a;Command failed: git checkout 2.2.0-c export NODE_HOME/usr/local/node-v14.14.0-linux-x64 npm config set registry https://registry.npmmirror.com 使用如上环…