十二、结构型(代理模式)
代理模式(Proxy Pattern)
概念
代理模式(Proxy Pattern)是一种结构型设计模式,代理对象在不改变目标对象功能的前提下,通过控制对目标对象的访问,来实现延迟加载、权限控制、日志记录等功能。代理模式可以通过提供一个中介对象,来控制客户端和实际目标对象之间的交互。
应用场景
-
远程代理(Remote Proxy):为位于不同地址空间的对象提供代理,解决远程调用问题。典型的例子是RMI(Remote Method Invocation),通过代理对象进行远程方法调用。
-
虚拟代理(Virtual Proxy):为某些需要较高开销的对象提供代理,延迟其加载和创建。比如,大型图片或文档的加载可以通过虚拟代理来进行,只有当真正需要时才创建实际对象。
-
保护代理(Protection Proxy):用于控制对对象的访问权限。代理对象检查调用者是否具有访问权限,只有在权限允许时,才能访问目标对象。
-
智能引用代理(Smart Reference Proxy):在访问对象时加入一些附加操作,如统计实际对象的引用次数、记录日志、检查锁等。
-
缓存代理(Caching Proxy):代理可以缓存某些计算结果或操作结果,使得后续的相同请求不需要重复计算,提升系统性能。
注意点
- 代理类和实际类必须实现相同的接口:代理类的设计应与目标类保持一致,使得客户端在使用代理类时与使用实际类没有差异。
- 性能开销:尽管代理模式可以增加功能,但也可能增加系统开销,尤其是远程代理涉及到网络传输时。
- 适合复杂的控制场景:代理模式尤其适合在需要控制访问、管理复杂操作、或添加额外处理逻辑的场景中使用。
核心要素
- Subject(抽象主题角色):定义代理类和目标类的共同接口,使得代理类和目标类可以被客户端通过相同的方式调用。
- RealSubject(真实主题角色):定义目标对象,实现真实的业务逻辑。
- Proxy(代理角色):负责控制对真实主题对象的访问,通常会引用一个真实主题对象,并通过实现抽象主题接口来代理其操作。
Java代码完整示例
代码示例:静态代理
// 抽象接口,定义真实主题和代理共同的行为
interface Subject {void request();
}// 真实主题,实现实际的业务逻辑
class RealSubject implements Subject {@Overridepublic void request() {System.out.println("真实主题执行请求");}
}// 代理类,控制对真实主题的访问
class Proxy implements Subject {private RealSubject realSubject;@Overridepublic void request() {// 代理可以在调用真实对象之前执行一些额外操作if (realSubject == null) {realSubject = new RealSubject();}System.out.println("代理:在调用真实对象前的额外操作");realSubject.request();System.out.println("代理:在调用真实对象后的额外操作");}
}// 客户端
public class Client {public static void main(String[] args) {Subject proxy = new Proxy();proxy.request();}
}
输出结果:
代理:在调用真实对象前的额外操作
真实主题执行请求
代理:在调用真实对象后的额外操作
各种变形用法完整示例
-
虚拟代理(Virtual Proxy)
虚拟代理的主要目的是推迟大开销对象的创建,直到需要使用时才创建。典型例子是延迟加载大型图片或文档。代码示例:
interface Image {void display(); }// 真实的图像类,实际加载图片 class RealImage implements Image {private String fileName;public RealImage(String fileName) {this.fileName = fileName;loadFromDisk();}private void loadFromDisk() {System.out.println("加载图片: " + fileName);}@Overridepublic void display() {System.out.println("显示图片: " + fileName);} }// 虚拟代理类 class ProxyImage implements Image {private RealImage realImage;private String fileName;public ProxyImage(String fileName) {this.fileName = fileName;}@Overridepublic void display() {if (realImage == null) {realImage = new RealImage(fileName);}realImage.display();} }// 客户端 public class VirtualProxyClient {public static void main(String[] args) {Image image = new ProxyImage("test.jpg");// 图片首次显示时加载image.display();System.out.println("");// 第二次调用时不需要加载image.display();} }输出结果:
加载图片: test.jpg 显示图片: test.jpg显示图片: test.jpg -
动态代理(Dynamic Proxy)
动态代理是在运行时创建代理类,而不是在编译时创建。在Java中,可以使用java.lang.reflect.Proxy类来实现动态代理,代理类可以动态代理接口定义的所有方法。代码示例:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;// 定义抽象接口 interface Service {void operation(); }// 实现接口的真实类 class RealService implements Service {@Overridepublic void operation() {System.out.println("执行实际操作");} }// 动态代理处理器 class DynamicProxyHandler implements InvocationHandler {private Object target;public DynamicProxyHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("动态代理:在方法执行前");Object result = method.invoke(target, args);System.out.println("动态代理:在方法执行后");return result;} }// 客户端 public class DynamicProxyClient {public static void main(String[] args) {RealService realService = new RealService();Service proxyInstance = (Service) Proxy.newProxyInstance(realService.getClass().getClassLoader(),realService.getClass().getInterfaces(),new DynamicProxyHandler(realService));proxyInstance.operation();} }输出结果:
动态代理:在方法执行前 执行实际操作 动态代理:在方法执行后 -
保护代理(Protection Proxy)
保护代理用于控制访问权限,可以通过代理来检查调用者是否具备权限,只有在通过检查时,才能调用目标对象的方法。代码示例:
interface BankAccount {void deposit(double amount);void withdraw(double amount); }class RealBankAccount implements BankAccount {private double balance;public RealBankAccount(double balance) {this.balance = balance;}@Overridepublic void deposit(double amount) {balance += amount;System.out.println("存款成功,当前余额: " + balance);}@Overridepublic void withdraw(double amount) {if (balance >= amount) {balance -= amount;System.out.println("取款成功,当前余额: " + balance);} else {System.out.println("余额不足,取款失败");}} }// 保护代理类 class ProtectionProxy implements BankAccount {private RealBankAccount realBankAccount;private String userRole;public ProtectionProxy(String userRole, double initialBalance) {this.userRole = userRole;realBankAccount = new RealBankAccount(initialBalance);}@Overridepublic void deposit(double amount) {realBankAccount.deposit(amount);}@Overridepublic void withdraw(double amount) {if ("ADMIN".equals(userRole)) {realBankAccount.withdraw(amount);} else {System.out.println("无权限取款");}} }// 客户端 public class ProtectionProxyClient {public static void main(String[] args) {BankAccount adminAccount = new ProtectionProxy("ADMIN", 1000);adminAccount.withdraw(500);BankAccount userAccount = new ProtectionProxy("USER", 1000);userAccount.withdraw(500);} }输出结果:
取款成功,当前余额: 500.0 无权限取款
总结
代理模式通过引入代理类为目标对象提供额外的功能或控制。它适用于延迟加载、权限控制、日志记录、远程调用等场景。代理模式可以分为静态代理和动态代理,并且具有多种变体,如虚拟代理、保护代理、远程代理等。
相关文章:
十二、结构型(代理模式)
代理模式(Proxy Pattern) 概念 代理模式(Proxy Pattern)是一种结构型设计模式,代理对象在不改变目标对象功能的前提下,通过控制对目标对象的访问,来实现延迟加载、权限控制、日志记录等功能。代…...
使用 python 下载 bilibili 视频
本文想要达成的目标为:运行 python 代码之后,在终端输入视频链接,可自动下载高清 1080P 视频并保存到相应文件夹。 具体可分为两大步:首先,使用浏览器开发者工具 F12 获取请求链接相关信息(根据 api 接口下…...
DevExpress WinForms中文教程:Data Grid - 如何点击获取信息?
在使用DevExpress WinForms的Data Grid之类控件时,可能需要实现自定义用户交互,例如显示数据行的上下文菜单,或者在双击一行时调用编辑表单。在这些情况下,您需要在指定的坐标处标识网格元素。 在本教程中,您将学习如…...
真空牛肉滚揉机的优点:
真空滚揉机是一种用于食品加工的设备,主要用于肉类深加工,如肉食制品和低温火腿的生产。 它通过滚揉、按压和腌制的过程,在真空状态下将原料肉与辅料、添加剂均匀混合,以提高肉制品的质量和口感。 真空滚揉机的工作原理是利…...
【物流配送中心选址问题】基于退火算法混合粒子群算法
课题名称: 基于退火算法混合粒子群算法的物流配送中心选址问题 改进方向:模拟退火算法优化粒子群算法 代码获取方式(付费): 模型说明: 待补充 Matlab仿真结果: 1. 模型优化后的仿真结果 2…...
elasticsearch 8.2 版本账号密码设置及SSL设置
背景:elasticsearch 8.2 设置账号密码-CSDN博客 failed to load SSL configuration does not contain any trusted certificate entries [2024-10-08T17:06:53,704][ERROR][o.e.b.ElasticsearchUncaughtExceptionHandler] [node-1] uncaught exception in thread [main] org…...
git gui基本使用
一、图形化界面 二、创建新项目 创建文件,加入暂存区,提交到版本库 三、创建分支 四、合并分支 1.切换至master 五、更新分支 六、解决冲突 修改冲突,加入暂存区,提交到版本库 七、远程创建库 Gitee - 基于 Git 的代码托管和研…...
从automaxprocs库浅窥Linux容器的资源控制
automaxprocs能够自动调整Go程序中的Goroutine数量,以充分利用系统资源并提高程序的性能。 automaxprocs通过读取系统信息,如CPU核心数和Cgroups限制,来动态调整Goroutine的数量 automaxprocs获取CPU限额的关键方法在 // CPUQuotaToGOMAXP…...
AI 读文献(二):综述论文10倍速读和整理
我是娜姐 迪娜学姐 ,一个SCI医学期刊编辑,探索用AI工具提效论文写作和发表。 上一篇娜姐讲了research研究论文的速度框架和效果, AI阅读研究论文,这个方法10倍速提升效率还不损失关键信息! 学员们反馈效果很好&#x…...
【AAOS】Android Automotive 10模拟器源码下载及编译
源码下载 repo init -u https://android.googlesource.com/platform/manifest -b android-10.0.0_r47 repo sync -c --no-tags --no-clone-bundle 源码编译 source build/envsetup.sh lunch aosp_car_x86_64-userdebug make -j8 运行效果 emualtor Cluster Home Map All …...
前端开发攻略---使用css实现滚动吸附效果
实现代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>Document</title><style>…...
解析 MySQL 查询优化:提升性能的十个关键策略
1. 避免全表扫描 当查询的数据量非常大时,全表扫描的效率会很低。应尽量通过在WHERE和ORDER BY涉及的列上创建索引,避免全表扫描。索引就像一本书的目录,可以快速定位到需要的数据,而不用从头开始逐页查找。 示例: 如…...
QT--QPushButton设置文本和图标、使能禁能、信号演示
按钮除了可以设置显示文本之外,还可以设置图标 文本 可以获取和设置按钮上显示的文本 // 获取和设置按钮的文本 QString text() const void setText(const QString &text)该属性,既可以在 Qt 设计师右侧的属性窗口中修改,也可以在代码…...
PostgreSQL学习笔记六:模式SCHEMA
模式(Schema) PostgreSQL中的模式(Schema)是一个命名的数据库对象集合,包括表、视图、索引、数据类型、函数、存储过程和操作符等。模式的主要作用是组织和命名空间数据库对象,使得同一个数据库中可以包含…...
基础IO -- 理解文件(1)
目录 一:回顾文件 二:加深对文件的理解 1.概念 2.以w写方式打开 3.以a追加方式打开 4.重定向 一:回顾文件 以前学习过在C语言中的文件操作, 但那根本是不足以理解文件的,即站在语言角度是不可能理解文件的 我们要…...
golang包管理
package 在工程化的Go语言开发项目中,Go语言的源码复用是建立在包(package)基础之上的。本文介绍了Go语言中如何定义包、如何导出包的内容及如何导入其他包。 包与依赖管理 本章学习目标 掌握包的定义和使用掌握init初始化函数的使用掌握…...
outlook 添加企业邮箱账号出现 553 authentication is required 错误解决
系统报错如下 问题原因 发件服务器身份验证设置错误,或者未勾选发送服务器验证的选项。 解决方案 Outlook客户端 本文以Outlook 2016为例,具体操如下: 1、在Outlook客户端的电子邮件设置窗口中,单击其他设置; 2、…...
一个开源可本地部署的英文翻译服务----EnToZhAPI
EnToZhAPI项目简介 项目背景 提供本地化的英文翻译服务API。支持单句翻译请求或者批量翻译请求。支持建立查询词汇表。 项目架构 前端:使用原生js,使用MDB作为CSS框架。django模板引擎渲染可视化界面。 后端:使用waitress作为后端服务器…...
【unity】编辑器扩展——在OnValidate中创建、删除游戏物体
我们知道在OnValidate中创建游戏物体会发出警告,删除游戏物体会报错。 所以我们使用协程,将开始动作的信号放在OnValidate中,将动作的执行放在帧结尾。 参考代码如下: using System.Collections; using UnityEngine;public clas…...
学习记录:js算法(六十四):最后一块石头的重量
文章目录 最后一块石头的重量思路一思路二 最后一块石头的重量 有一堆石头,每块石头的重量都是正整数。 每一回合,从中选出两块 最重的 石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x < y。那么粉碎的可能结果如…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
