二十三种设计模式-代理模式

server/2025/1/20 5:37:05/

一、定义与核心思想

代理模式是一种结构型设计模式,其核心思想是提供一个代理对象,用以控制对实际对象的访问。通过代理对象,可以在不改变实际对象的情况下,添加各种功能,如权限控制、懒加载、缓存、远程调用等。

二、组成要素

代理模式主要由以下几个要素组成:

  1. 抽象主题(Subject)

    • 这是一个接口或抽象类,定义了代理对象和实际对象共同的接口,使得代理对象可以在客户端中替代实际对象使用。

    • 例如,定义一个Subject接口,其中声明了request()方法。

  2. 实际主题(RealSubject)

    • 实现抽象主题接口的具体类,定义了代理所代表的真实对象。实际主题对象实现了业务逻辑,是客户端最终需要调用的对象。

    • 例如,RealSubject类实现了Subject接口,在request()方法中实现了具体的业务逻辑。

  3. 代理(Proxy)

    • 也实现了抽象主题接口,内部包含一个对实际主题对象的引用。代理对象在调用实际主题对象的方法前后可以添加额外的逻辑,从而控制对实际主题对象的访问。

    • 例如,Proxy类实现了Subject接口,并且有一个RealSubject类型的成员变量。在request()方法中,代理对象可以进行权限检查、日志记录等操作,然后调用实际主题对象的request()方法。

三、实现示例

以下是使用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() {preRequest();if (realSubject == null) {realSubject = new RealSubject();}realSubject.request();postRequest();}private void preRequest() {System.out.println("代理对象的预处理操作,如权限检查");}private void postRequest() {System.out.println("代理对象的后处理操作,如日志记录");}
}// 客户端代码
public class Client {public static void main(String[] args) {Subject proxy = new Proxy(); // 直接创建代理对象proxy.request(); // 通过代理对象调用方法}
}

在这个示例中,客户端通过代理对象proxy调用request()方法。代理对象在调用实际主题对象RealSubjectrequest()方法前后,分别进行了预处理和后处理操作,如权限检查和日志记录。

四、优点

  1. 职责分离

    • 代理对象可以将客户端与实际主题对象解耦,使得客户端不需要直接与实际主题对象交互,从而降低了系统的耦合度。

  2. 功能扩展

    • 代理对象可以在不修改实际主题对象的情况下,添加各种功能,如权限控制、缓存、懒加载等,符合开闭原则。

  3. 控制访问

    • 代理对象可以控制对实际主题对象的访问,例如,根据用户的权限决定是否允许调用实际主题对象的方法。

  4. 远程调用

    • 代理对象可以处理远程方法调用的细节,如网络通信、序列化/反序列化等,使得客户端可以透明地调用远程对象的方法。

  5. 延迟加载

    • 代理对象可以实现懒加载,只有在第一次调用方法时才创建实际主题对象,从而提高系统的性能。

五、缺点

  1. 增加复杂性

    • 代理模式会增加系统的复杂性,因为需要创建额外的代理类和管理代理对象与实际对象之间的关系。

  2. 性能开销

    • 代理对象的调用会增加额外的性能开销,特别是当代理对象的逻辑比较复杂时,可能会对系统的性能产生一定的影响。

六、应用场景

  1. 权限控制

    • 通过代理对象可以控制对实际对象的访问权限,例如,根据用户的权限决定是否允许调用某个方法或访问某个资源。

    • 例如,实现一个权限控制代理,只有具备相应权限的用户才能通过代理对象调用实际对象的方法。

  2. 远程调用

    • 代理对象可以处理远程方法调用的细节,使得客户端可以透明地调用远程服务器上的方法。

    • 例如,使用RMI(Remote Method Invocation)技术实现远程对象调用,客户端通过代理对象调用远程服务器上的方法。

  3. 懒加载

    • 代理对象可以实现懒加载,只有在第一次调用方法时才创建实际对象,从而提高系统的性能。

    • 例如,加载一个大型的数据库连接对象时,可以使用懒加载代理来延迟创建连接对象,直到真正需要使用时才创建。

  4. 缓存

    • 代理对象可以缓存方法的调用结果,当再次调用相同的方法时,直接返回缓存的结果,从而提高系统的性能。

    • 例如,缓存网页的静态内容,当用户再次请求相同的内容时,直接从缓存中获取,而不需要重新生成。

  5. 日志记录

    • 代理对象可以在调用实际对象的方法前后记录日志,用于监控和调试。

    • 例如,记录方法的调用时间、参数、返回值等信息,帮助开发者进行问题定位和性能优化。

七、动态代理

除了静态代理,Java还支持动态代理。动态代理可以在运行时动态创建代理对象,而不需要在编译时定义代理类。动态代理主要使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现。

动态代理示例
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;// 抽象主题接口
interface Subject {void request();
}// 实际主题类
class RealSubject implements Subject {@Overridepublic void request() {System.out.println("实际对象的请求");}
}// 调用处理器
class MyInvocationHandler implements InvocationHandler {private Object target;public MyInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {preRequest();Object result = method.invoke(target, args);postRequest();return result;}private void preRequest() {System.out.println("代理对象的预处理操作,如权限检查");}private void postRequest() {System.out.println("代理对象的后处理操作,如日志记录");}
}// 客户端代码
public class Client {public static void main(String[] args) {Subject realSubject = new RealSubject(); // 创建实际主题对象MyInvocationHandler handler = new MyInvocationHandler(realSubject); // 创建调用处理器并传入实际主题对象Subject proxy = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(), // 目标类加载器new Class<?>[]{Subject.class}, // 目标类实现的接口handler // 调用处理器);proxy.request(); // 通过代理对象调用方法}
}

在这个示例中,客户端通过Proxy.newProxyInstance方法动态创建了一个代理对象proxy。代理对象的调用处理器MyInvocationHandler在调用实际主题对象RealSubject的方法前后,分别进行了预处理和后处理操作。

总结

代理模式通过代理对象控制对实际对象的访问,可以在不修改实际对象的情况下,添加各种功能,如权限控制、懒加载、缓存、远程调用等。代理模式在实际开发中非常有用,选择静态代理还是动态代理取决于具体的需求和场景。


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

相关文章

数据结构题目 课时7

题目 1、由树转换成二叉树&#xff0c;其根的右孩子指针总是空的。&#xff08; &#xff09; 2、画出下列二叉树对应的森林。 3、设森林 F 对应的二叉树为 B&#xff0c;它有 m 个结点&#xff0c;B 的根为 P&#xff0c;P 的右子树的结点个数为 n&#xff0c;森林 F 中第一…

js中处理树形数据

平时开发中会经常碰到树形数据&#xff0c;而我们存储的数据往往是扁平的数据&#xff0c;需要在前端或者后端对数据进行进一步处理成前端组件需要的树形数据。在操作完树形数据后再转换成扁平数据发给后端。下面JavaScript 中处理树形数据的方法&#xff0c;包括构建树和解析树…

Android CustomTextField

在 Compose 中开发用户界面时&#xff0c;需要处理输入框和键盘的交互&#xff0c;例如在键盘弹出时调整布局位置&#xff0c;避免遮挡重要内容。本篇博客将通过一个完整的示例展示如何实现这一功能。 功能概述 本例实现了一个简单的输入框。当输入框获得焦点或输入文字时&…

QT 如何禁止QComboBox鼠标滚轮

一般情况下&#xff0c;QComboBox会相应鼠标的滚轮事件&#xff0c;即当鼠标停靠在QComboBox上方时&#xff0c;滚动鼠标滚轮&#xff0c;QComboBox的选项会发生切换。但这或许并不是我们希望所出现的&#xff0c;尤其是当QComboBox嵌入在QScrollArea中时&#xff0c;用户只是想…

【MATLAB】subplot如何增加title

在 Matlab 中&#xff0c;使用 subplot 函数将图形窗口划分为多个子图&#xff0c;并使用 title 函数为每个子图添加标题。以下是一个示例&#xff1a; matlab % 生成示例数据 x 0:0.1:10; y1 sin(x); y2 cos(x); % 创建一个 2 行 1 列的子图布局&#xff0c;并选…

通信协议之多摩川编码器协议

前言 学习永无止境&#xff01;本篇是通信协议之多摩川编码器协议&#xff0c;主要介绍RS485硬件层以及软件层帧格式。 注&#xff1a;本文章为学习笔记&#xff0c;部分图片与文字来源于网络/应用手册&#xff0c;如侵权请联系&#xff01;谢谢&#xff01; 一、多摩川协议概述…

多包单仓库(monorepo)实现形式

目录 背景 需求和方案 从0开始搭建一个Monorepo项目 创建 配置全局公共样式 配置全局公共组件 方式1:不需要独立发布的组件包,只在当前项目的子项目中使用 方式2:需要独立发布和版本维护的包 子项目的独立构建和部署 总结 Monorepo优势 便于代码维护、管理 支持…

【MySQL】简单解析一条SQL查询语句的执行过程

1. MySQL 的逻辑架构图 MySQL 架构主要分为 Server 层和存储引擎层。Server 层集成了连接器、查询缓存、分析器、优化器和执行器等核心组件&#xff0c;负责提供诸如日期、时间、数学和加密等内置函数&#xff0c;以及实现存储过程、触发器、视图等跨存储引擎的功能。存储引擎层…