十二、结构型(代理模式)
代理模式(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。那么粉碎的可能结果如…...

单片机探秘:从理论到应用
单片机探秘:从理论到应用 在这个科技飞速发展的时代,单片机的应用如同一颗璀璨的星星,照亮了我们生活的方方面面。今天,让我们一同深入探讨单片机的原理与应用,揭开这个技术领域的神秘面纱。 1. 单片机概述 1.1 什么…...

options妙用
options妙用 设置默认浏览器为 Chrome options(browser “chrome”) 再次尝试运行 igsva() res <- igsva() 加载 BiocManager library(BiocManager) 设置超时时间 options(timeout 3600) 安装包 BiocManager::install(c(“org.Hs.eg.db”, “org.Mm.eg.db”)) …...

UE5 圆周运动、贝塞尔曲线运动、贝塞尔曲线点
圆周运动 贝塞尔曲线路径运动 蓝图函数库创建贝塞尔曲线点 // Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h" #include "Kismet/BlueprintFunctionLibrary.h" #include "MyB…...

线程局部存储(TLS)
很多时候,我们可能想存储一些线程的私有数据,属于线程的私有变量有局部变量,函数的参数,假如我们要在线程中存储全局变量,多个线程访问都对这个变量有自己的一个副本。 一、隐式实现 __thread int a; //linux __dec…...

JavaSE——集合7:Set接口实现类—TreeSet
目录 一、TreeSet基本介绍 二、TreeSet核心方法 三、TreeSet排序方法 四、TreeSet源码解析 1.无参构造时,底层是创建TreeMap对象 2.有参构造时,底层也创建TreeMap对象 3.执行add方法 4.执行put方法 一、TreeSet基本介绍 TreeSet是 Java 集合框架…...

【idea技巧篇】idea的类注释和方法注释模版自定义设置
这块idea技巧虽然常用,谁没事会经常修改模版设置呢,一般是搭建开发环境的时候或者开发规范要求等设置一次就行了。用的虽然少,但几乎每次搭建环境都会用到,这里记录下并分享设置的过程已经发现的更高级的一些使用技巧。 注释模版…...

【Kubernetes① 基础】一、容器基础
目录 一、进程二、隔离与限制三、容器镜像总结参考书籍 一、进程 容器技术的兴起源于PaaS技术(平台即服务)的普及;Docker公司发布的Docker项目具有里程碑式的意义;Docker项目通过“容器镜像”解决了应用打包这个根本性难题(CloudFoundry)。 容器本身的价…...

计算机网络第1章(概述)万字笔记详细版
1.1、计算机网络在信息时代的作用 计算机网络已由一种通信基础设施发展成为一种重要的信息服务基础设施计算机网络已经像水,电,煤气这些基础设施一样,成为我们生活中不可或缺的一部分 我国互联网发展状况 中国互联网络信息中心CNNIC 1.2、…...

每日一练算法题(堆串的基本操作StrReplace(S, T, V))
6-2 堆串的基本操作StrReplace(S, T, V) 编写算法,实现堆串的基本操作StrReplace(S, T, V)。 初始条件: 串S, T和 V 均已存在,且 V 是非空串。 操作结果: 用V替换主串S中出现的所有与(模式串)T相等的不重叠的子串。输入格式: 第一行:S 第二行&#…...

IRP默认最小流程
IRP是Windows内核中的一种非常重要的数据结构。上层应用程序与底层驱动程序通信时,应用程序会发出I/O请求,操作系统将相应的I/O请求转换成相应的IRP,不同的IRP会根据类型被分派到不同的派遣例程中进行处理。 irp相当于R3下的消息,…...