23种设计模式——代理模式(Proxy Pattern)详解

✅作者简介:大家好,我是 Meteors., 向往着更加简洁高效的代码写法与编程方式,持续分享Java技术内容。
🍎个人主页:Meteors.的博客
💞当前专栏:设计模式
✨特色专栏:知识分享
🥭本文内容:23种设计模式——代理模式(Proxy Pattern)
📚 ** ps **  :阅读文章如果有问题或者疑惑,欢迎在评论区提问或指出。


目录

一. 背景

二. 介绍

三. 核心概念

四. 实际应用示例

1. 图片加载代理

2. 权限控制代理

3. 网络请求代理

4. 缓存代理

五. 代理模式的类型

六. 代理模式的优势

七. 注意事项

八. 总结


一. 背景

也行此时你还感觉不到代理模式是多么的好用,我慢慢帮你剖析吧!

不知道读者对于Java的Spring AOP(方法粒度的面向切面编程)是否了解?其实代理模式可以类比成一种对象粒度的AOP。在我们在项目中创建一个对象的时候,如果还要添加类似于日志、判断前置条件(如android中的用户是否允许某种权限)、或者进行缓存判断(类似Redis)的操作,如果这个时候如果都把代码写成一团,功能肯定也可以实现,但是代码会显得非常冗余,这个时候代理模式的优势就体现出来了,让我们一起来揭开它的面纱!


二. 介绍

代理模式(Proxy Pattern)是面向对象设计模式中的一种结构型模式。它为其他对象提供一种代理以控制对这个对象的访问。代理模式在客户端和目标对象之间起到中介作用,可以在不改变目标对象的情况下,增加额外的功能操作(知道是结构型和可以添加功能就够了~)。


三. 核心概念

在代理模式中,通常涉及三个角色:
抽象主题(Subject):声明了真实主题和代理主题的共同接口方法功能提到接口
真实主题(RealSubject):定义了代理主题所代表的真实对象原本要New的对象
代理主题(Proxy):保存一个引用使得代理可以访问实体,并提供一个与Subject的接口相同的接口完成延迟加载和添加日志等功能,后面使用对象的时候只要创建这个对象就够了


四. 实际应用示例

1. 图片加载代理

在Android应用中,我们经常需要加载网络图片。为了提高性能,可以使用代理模式来实现图片的延迟加载:

// 抽象接口
public interface Image {void display();
}// 真实主题 - 真实图片
public class RealImage implements Image {private String filename;public RealImage(String filename) {this.filename = filename;loadFromDisk();}private void loadFromDisk() {System.out.println("从磁盘加载图片: " + filename);// 模拟耗时操作try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}@Overridepublic void display() {System.out.println("显示图片: " + filename);}
}// 代理主题 - 图片代理
public class ImageProxy implements Image {private RealImage realImage;private String filename;public ImageProxy(String filename) {this.filename = filename;}@Overridepublic void display() {if (realImage == null) {realImage = new RealImage(filename);}realImage.display();}
}// 使用示例
public class MainActivity {public void onCreate() {// 创建代理对象,不会立即加载图片Image image = new ImageProxy("photo.jpg");// 只有在需要显示时才真正加载图片image.display(); // 此时才加载并显示图片}
}

这种方式可以有效减少内存使用,只有在真正需要显示图片时才加载,提高了应用性能。

2. 权限控制代理

在Android应用中,某些功能需要特定权限才能访问,可以使用代理模式进行权限检查:

// 抽象接口
public interface Camera {void takePhoto();void recordVideo();
}// 真实主题 - 相机功能实现
public class RealCamera implements Camera {@Overridepublic void takePhoto() {System.out.println("拍照成功");}@Overridepublic void recordVideo() {System.out.println("开始录像");}
}// 代理主题 - 带权限检查的相机代理
public class CameraPermissionProxy implements Camera {private RealCamera realCamera;private Context context;public CameraPermissionProxy(Context context) {this.context = context;}@Overridepublic void takePhoto() {if (checkCameraPermission()) {if (realCamera == null) {realCamera = new RealCamera();}realCamera.takePhoto();} else {System.out.println("没有相机权限,无法拍照");}}@Overridepublic void recordVideo() {if (checkCameraPermission()) {if (realCamera == null) {realCamera = new RealCamera();}realCamera.recordVideo();} else {System.out.println("没有相机权限,无法录像");}}private boolean checkCameraPermission() {// 检查相机权限return ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED;}
}// 使用示例
public class CameraActivity {private Camera camera;public void onCreate() {camera = new CameraPermissionProxy(this);}public void onTakePhotoClick() {camera.takePhoto(); // 自动检查权限}
}

3. 网络请求代理

在网络请求中,可以使用代理模式添加日志、缓存等功能:

// 抽象接口
public interface HttpClient {String get(String url);String post(String url, String data);
}// 真实主题 - 真实的HTTP客户端
public class RealHttpClient implements HttpClient {@Overridepublic String get(String url) {// 模拟网络请求System.out.println("发送GET请求到: " + url);return "Response from " + url;}@Overridepublic String post(String url, String data) {System.out.println("发送POST请求到: " + url + " 数据: " + data);return "Response from " + url;}
}// 代理主题 - 带日志功能的HTTP客户端代理
public class LoggingHttpClientProxy implements HttpClient {private RealHttpClient realHttpClient;private boolean enableLogging;public LoggingHttpClientProxy(boolean enableLogging) {this.enableLogging = enableLogging;}@Overridepublic String get(String url) {long startTime = System.currentTimeMillis();if (realHttpClient == null) {realHttpClient = new RealHttpClient();}if (enableLogging) {System.out.println("开始GET请求: " + url);}String result = realHttpClient.get(url);if (enableLogging) {long endTime = System.currentTimeMillis();System.out.println("GET请求完成,耗时: " + (endTime - startTime) + "ms");}return result;}@Overridepublic String post(String url, String data) {long startTime = System.currentTimeMillis();if (realHttpClient == null) {realHttpClient = new RealHttpClient();}if (enableLogging) {System.out.println("开始POST请求: " + url);}String result = realHttpClient.post(url, data);if (enableLogging) {long endTime = System.currentTimeMillis();System.out.println("POST请求完成,耗时: " + (endTime - startTime) + "ms");}return result;}
}// 使用示例
public class NetworkManager {private HttpClient httpClient;public NetworkManager() {// 开发环境启用日志boolean isDebug = true;httpClient = new LoggingHttpClientProxy(isDebug);}public void fetchData() {String result = httpClient.get("https://api.example.com/data");System.out.println("获取到数据: " + result);}
}

4. 缓存代理

在数据访问中,代理模式可以用来实现缓存机制:

// 抽象接口
public interface DataService {String getData(String key);
}// 真实主题 - 真实的数据服务
public class RealDataService implements DataService {@Overridepublic String getData(String key) {// 模拟从数据库或网络获取数据System.out.println("从数据库获取数据,键: " + key);try {Thread.sleep(1000); // 模拟耗时操作} catch (InterruptedException e) {e.printStackTrace();}return "Data for " + key;}
}// 代理主题 - 带缓存功能的数据服务代理
public class CachedDataServiceProxy implements DataService {private RealDataService realDataService;private Map<String, String> cache = new HashMap<>();@Overridepublic String getData(String key) {// 先检查缓存if (cache.containsKey(key)) {System.out.println("从缓存获取数据,键: " + key);return cache.get(key);}// 缓存未命中,从真实服务获取if (realDataService == null) {realDataService = new RealDataService();}String data = realDataService.getData(key);cache.put(key, data); // 存入缓存System.out.println("数据已缓存,键: " + key);return data;}public void clearCache() {cache.clear();System.out.println("缓存已清空");}
}// 使用示例
public class DataRepository {private DataService dataService = new CachedDataServiceProxy();public void displayData(String key) {// 第一次调用String data1 = dataService.getData(key);System.out.println("第一次获取: " + data1);// 第二次调用(从缓存获取)String data2 = dataService.getData(key);System.out.println("第二次获取: " + data2);}
}

五. 代理模式的类型

代理模式还可以根据作用,分为下面几种类型:

远程代理(Remote Proxy):为一个位于不同地址空间的对象提供本地代表。

虚拟代理(Virtual Proxy):根据需要创建开销很大的对象。

保护代理(Protection Proxy):控制对原始对象的访问,常用于对象应该有不同的访问权限时。

智能引用(Smart Reference):取代简单的指针,在访问对象时执行一些附加操作。


六. 代理模式的优势

延迟加载:虚拟代理可以实现对象的延迟初始化,节省系统资源
权限控制:保护代理可以在访问真实对象前进行权限检查
功能增强:可以在不修改原始类的情况下添加额外功能(如日志、缓存等)
远程访问:远程代理可以隐藏网络通信的复杂性
对象访问控制:可以控制对昂贵资源的访问


七. 注意事项

性能开销:代理模式会增加系统的复杂性和可能的性能开销
代码复杂性:引入代理会增加类的数量,使系统更加复杂
调试困难:代理模式可能使调试变得更加困难
合理使用:不是所有场景都需要代理,应根据实际需求选择


八. 总结

代理模式是开发中非常实用的设计模式,它可以在不改变原有代码结构的情况下,为对象提供额外的功能。通过合理使用代理模式,我们可以实现延迟加载、权限控制、日志记录、缓存等功能,提高应用的性能和用户体验。
在实际开发中,我们可以根据具体需求选择合适的代理类型,如虚拟代理用于延迟加载,保护代理用于权限控制,智能引用代理用于添加额外功能等。正确使用代理模式可以让我们的代码更加优雅、高效和易于维护!


最后,

        其它设计模式会陆续更新,希望文章对你有所帮助!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/98326.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/98326.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

webpack scope hositing 和tree shaking

Scope Hoisting&#xff08;作用域提升&#xff09; 和 Tree Shaking&#xff08;摇树优化&#xff09; 是现代前端构建中至关重要的概念。它们是构建工具&#xff08;如 Webpack、Rollup、Vite&#xff09;用来优化最终打包产物的核心技术。 核心概念快速理解 Tree Shaking&am…

手写React状态hook

在日常开发中&#xff0c;我们经常用到 React 的状态管理 Hook&#xff1a;useState 和 useReducer。 但你有没有想过&#xff1a;这些 Hook 内部是怎么实现的&#xff1f;为什么调用 setState 之后组件会重新渲染&#xff1f; 今天我们就来从零手写 useState 和 useReducer&am…

力扣hot100:相交链表与反转链表详细思路讲解(160,206)

问题描述核心思路&#xff1a;双指针交替遍历算法思想&#xff1a; 使用两个指针 pa 和 pb 分别从链表A和链表B的头节点出发&#xff0c;同步向后遍历。当任一指针走到链表末尾时&#xff0c;将其重定位到另一链表的头节点继续遍历。若两链表相交&#xff0c;pa 和 pb 最终会在…

跨平台游戏引擎 Axmol-2.8.1 发布

所有使用 axmol-2.8.0 的开发者都应更新至此版本 Axmol 2.8.1 版本是一个以错误修复和功能改进为主的次要 LTS 长期支持版本&#xff0c;发布时间: 2025 年 9 月 5 日 &#x1f64f;感谢所有对 axmol 项目的贡献者&#xff0c;包括财务赞助者&#xff1a;scorewarrior、peter…

通过PXE的方式实现Ubuntu 24.04 自动安装

PXE自动化安装Ubuntu 24.04的配置文件之前都是通过PXE来自动化安装Redhat系列的&#xff0c;例如&#xff1a;Rocky9、Rocky10、CentOS7、银河麒麟 Kylin-V10、Kylin-V11、OpenEuler 24.03等。现在安装Ubuntu系列的跟红帽的不太一样&#xff0c;所以在这里介绍下。创建三个文件…

AOSP Framework开发的一些超方便的快捷命令

在系统源码中发现的一些命令和快捷方式。我们在编译源码之前执行的source build/envsetup.sh,通过cat build/envsetup.sh发现如下命令 - lunch: lunch <product_name>-<build_variant>Selects <product_name> as the product to build, and <build_…

【Protues仿真】基于AT89C52单片机的数码管驱动事例

目录 0案例视频效果展示 1 AT89C52单片机驱动单个数码管 1.1 数码管基础知识 1.1.1外观与引脚 1.1.2 共阴(CC) vs 共阳(CA) 1.1.3段码表(以数字1为例) 1.1.4驱动方式A. 直连IO(最简单,占用IO多)一个段一根线,共阴或共阳公共端固定接GND/VCC。适合单个数码管、…

01-Redis 发展简史与核心定位解析:从诞生到三大产品矩阵

目录引言一、Redis 的起源与发展&#xff1a;从定制工具到开源生态二、Redis 的核心定位&#xff1a;不止是缓存的多面手三、Redis 三大产品矩阵&#xff1a;按需选择的完整解决方案3.1 Redis Open Source&#xff08;社区版&#xff09;&#xff1a;入门与轻量场景首选3.2 Red…

记录jilu~

centos1、安装最小版Linux 安装必要工具yum -install -y epel-releaseyum -install -y net-toolsyum -install -y vim2、修改hostname hostnamectl net-hostname newhostname3、网络配置文件&#xff0c;网关 &#xff0c; 使用ip &#xff0c;dns。。/etc/sysconfig/network-s…

【Linux基础】fdisk命令详解:从入门到精通的磁盘分区管理完全指南

目录 前言 1 fdisk命令概述 1.1 什么是fdisk 1.2 fdisk的应用场景 1.3 fdisk与其他分区工具的比较 2 fdisk命令的安装与基本语法 2.1 在不同Linux发行版中安装fdisk 2.2 fdisk的基本语法 3 fdisk命令参数详解 3.1 主要参数说明 3.2 交互式命令 4 fdisk操作流程详解…

Flowable 工作流引擎

1、核心类 Flowable 引擎通过 ProcessEngine 作为总入口点&#xff0c;提供了多个核心服务接口&#xff0c;每个服务都负责特定的功能领域&#xff1a;服务名称 (Service Name)主要功能 (Main Functionality)关键操作 (Key Operations)RepositoryService管理流程定义和部署&…

(RDFS)随机深度特征选择方法解释:简而言之,RDFS主要针对的是恶意的服务器,它建立在客户端是诚实的前提下。

1. 随机深度特征选择是怎么实现的&#xff1f;随机深度特征选择 是一种在分布式机器学习&#xff08;特别是联邦学习&#xff09;中用于保护客户端数据隐私的技术。它的核心思想是&#xff1a;在每一轮训练中&#xff0c;每个客户端随机选择模型的一个子集&#xff08;即“深度…

C++20格式化字符串:std::format的使用与实践

在C编程中&#xff0c;字符串格式化是一项常见的任务。在C20引入std::format之前&#xff0c;开发者通常依赖于一些传统的解决方案&#xff0c;如printf系列函数、sstream&#xff0c;或者第三方库如boost.format。然而&#xff0c;这些方法在代码可读性、类型安全性和灵活性方…

【漏洞复现】CVE-2025-8088|WinRAR 路径穿越漏洞:从原理到蓝屏攻击全流程

【漏洞复现】CVE-2025-8088&#xff5c;WinRAR 路径穿越漏洞&#xff1a;从原理到蓝屏攻击全流程 前言 WinRAR 作为 Windows 平台最常用的压缩管理工具之一&#xff0c;几乎是每台电脑的 “标配软件”。但在 2025 年 8 月&#xff0c;一款影响范围覆盖 WinRAR 0 至 7.12 全版本…

uniapp中使用echarts并且支持pc端的拖动、拖拽和其他交互事件

npm install echarts -D ​ // "echarts": "^5.3.2", [推荐版本] // "zrender": "^5.3.2" [如果报错的话就安装这个]<template><view class"container"><view id"myChart" class"chart"…

Qt中QProxyStyledrawControl函数4个参数的意义

Qt中QProxyStyle::drawControl函数4个参数的意义 我们来详细解释一下 Qt 中 QProxyStyle::drawControl 函数的四个参数。 这个函数是 Qt 样式系统中的一个核心方法&#xff0c;用于绘制标准 UI 元素&#xff08;如按钮、复选框、菜单栏等&#xff09;。当你继承 QProxyStyle 并…

idf-esp32 PWM呼吸灯(LEDC头文件)

相关宏和变量#define LED_PIN GPIO_NUM_3 #define LEDC_CHANNEL LEDC_CHANNEL_0 #define LEDC_TIMER LEDC_TIMER_0 #define LEDC_MODE LEDC_LOW_SPEED_MODE #define LEDC_DUTY_RES LEDC_TIMER_13_BIT // 2^13 8192级亮度 #define LEDC_FREQUENCY 50…

PLC_博图系列☞基本指令”S_ODTS:分配保持型接通延时定时器参数并启动“

PLC_博图系列☞基本指令”S_ODTS&#xff1a;分配保持型接通延时定时器参数并启动“ 文章目录PLC_博图系列☞基本指令”S_ODTS&#xff1a;分配保持型接通延时定时器参数并启动“背景介绍S_ODTS&#xff1a; 分配保持型接通延时定时器参数并启动说明参数脉冲时序图示例关键字&a…

OneCode 可视化揭秘系列(三):AI MCP驱动的智能工作流逻辑编排

OneCode 可视化揭秘系列&#xff08;三&#xff09;&#xff1a;AI MCP驱动的智能工作流逻辑编排 引言 在前两篇系列博文中&#xff0c;我们详细探讨了OneCode可视化动作的基础配置与界面设计&#xff0c;以及组件交互与数据流管理。在本篇文章中&#xff0c;我们将深入剖析逻辑…

TypeORM、Sequelize、Hibernate 的优缺点对比:新手常见 SQL 与 ORM 踩坑总结

1. ORM 与关系型数据库&#xff08;MySQL、PostgreSQL&#xff09; 的使用 SQL 语句编写&#xff08;JOIN、GROUP BY、索引使用、事务控制&#xff09;与 ORM 映射&#xff08;如 Sequelize、TypeORM、Hibernate&#xff09;之间的差异会让新手非常纠结&#xff1b;尤其是理解…