设计模式 结构型 代理模式(Proxy Pattern)与 常见技术框架应用 解析

news/2025/1/8 19:01:01/

在这里插入图片描述

代理模式(Proxy Pattern)是一种常见的设计模式,在软件开发中有着广泛的应用。其核心思想是通过创建一个代理类来控制对另一个对象的访问,从而实现对目标对象功能的扩展、保护或其他附加操作。

一、核心思想

代理模式的核心思想在于提供一个代理对象,作为客户端与目标对象之间的中介。客户端并不直接调用目标对象的方法,而是通过调用代理对象的方法来间接调用目标对象的方法。这样可以在调用目标对象方法之前或之后添加额外的逻辑,如权限检查、日志记录、事务处理等。

二、定义与结构

定义代理模式为其他对象提供一种代理以控制对这个对象的访问。

结构

  • 抽象角色(Subject):通过接口或抽象类声明真实角色实现的业务方法。
  • 代理角色(Proxy):实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
  • 真实角色(Real Subject):实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

角色

代理模式中,通常涉及以下三个角色:

  • 抽象主题(Subject):定义了真实主题和代理主题的共同接口。
  • 真实主题(Real Subject):实现了具体的业务逻辑,是代理模式中被代理的对象。
  • 代理主题(Proxy Subject):持有对真实主题的引用,可以在调用真实主题之前或之后执行附加的操作。

三、实现步骤及代码示例

以下是通过Java技术框架实现代理模式的详细步骤及代码示例:

静态代理

  1. 定义抽象主题接口:
public interface Subject {void request();
}
  1. 实现真实主题类:
public class RealSubject implements Subject {@Overridepublic void request() {System.out.println("RealSubject: Handling request.");}
}
  1. 实现代理主题类:
public class ProxySubject implements Subject {private RealSubject realSubject;@Overridepublic void request() {if (realSubject == null) {realSubject = new RealSubject();}preRequest();realSubject.request();postRequest();}private void preRequest() {System.out.println("ProxySubject: Preparing for request.");}private void postRequest() {System.out.println("ProxySubject: Finishing up request.");}
}
  1. 客户端代码:
public class Client {public static void main(String[] args) {Subject subject = new ProxySubject();subject.request();}
}

动态代理

  1. 定义抽象主题接口(同上)。
  2. 实现真实主题类(同上)。
  3. 创建InvocationHandler实现类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class ProxyHandler implements InvocationHandler {private final Object target;public ProxyHandler(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("ProxySubject: Preparing for request.");}private void postRequest() {System.out.println("ProxySubject: Finishing up request.");}
}
  1. 客户端代码:
import java.lang.reflect.Proxy;public class Client {public static void main(String[] args) {RealSubject realSubject = new RealSubject();InvocationHandler handler = new ProxyHandler(realSubject);Subject subject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),realSubject.getClass().getInterfaces(),handler);subject.request();}
}

四、常见技术框架应用

1、网络请求代理服务器

在网络请求中,可能会使用代理服务器。假设我们有一个简单的网络请求接口和实现:

  • 定义抽象主题(网络请求接口):
interface NetworkRequest {String request(String url);
}
  • 真实主题(实际的网络请求实现):
class RealNetworkRequest implements NetworkRequest {@Overridepublic String request(String url) {System.out.println("Sending request to " + url);// 这里可以是实际的网络请求代码,比如使用HttpURLConnection等return "Response from " + url;}
}
  • 代理(带有缓存功能的网络请求代理):
import java.util.HashMap;
import java.util.Map;class CachedNetworkRequestProxy implements NetworkRequest {private RealNetworkRequest realRequest;private Map<String, String> cache = new HashMap<>();@Overridepublic String request(String url) {if (cache.containsKey(url)) {System.out.println("Retrieving from cache for " + url);return cache.get(url);} else {if (realRequest == null) {realRequest = new RealNetworkRequest();}String response = realRequest.request(url);cache.put(url, response);return response;}}
}
  • 使用示例:
NetworkRequest request = new CachedNetworkRequestProxy();
System.out.println(request.request("https://example.com"));
System.out.println(request.request("https://example.com"));

这里代理对象CachedNetworkRequestProxy在第一次请求时会调用真实对象RealNetworkRequest进行网络请求,然后将结果缓存起来。之后相同的请求就直接从缓存中获取,减少了网络请求的次数。

2、Spring AOP

在Java中,代理模式也很常见,特别是在Spring框架中。以下是一个简单的Spring AOP(面向切面编程)示例:

import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;// Service interface
public interface SimpleService {void perform();
}// Service implementation
public class SimpleServiceImpl implements SimpleService {@Overridepublic void perform() {System.out.println("Performing service operation");}
}// Before advice
public class LoggingBeforeAdvice implements MethodBeforeAdvice {@Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {System.out.println("Logging before method: " + method.getName());}
}// Client code
public class ProxyDemo {public static void main(String[] args) {SimpleService service = new SimpleServiceImpl();ProxyFactory factory = new ProxyFactory();factory.setTarget(service);factory.addAdvice(new LoggingBeforeAdvice());SimpleService proxy = (SimpleService) factory.getProxy();proxy.perform(); // Output: Logging before method: perform \n Performing service operation}
}

在这个例子中,ProxyFactory创建了一个代理对象,并在方法调用之前添加了日志记录功能。

在Java中,代理模式通常用于远程方法调用(RMI)、JDK动态代理或CGLIB代理。在.NET中,可以用Castle DynamicProxy库来创建代理。而在Python中,wrapt库提供了强大的装饰器和代理功能。在C#中,可以使用RealProxy类来实现动态代理。这些技术框架中的代理模式实现方式虽然有所不同,但核心思想都是相同的,即通过代理对象来控制对目标对象的访问。

五、应用场景

代理模式的应用场景非常广泛,包括但不限于以下几个方面:

  1. 远程代理:为其他对象提供一种代理以控制对这个对象的远程访问。例如,在分布式系统中,客户端可以通过代理对象来访问远程服务器上的对象。
  2. 虚拟代理:根据需要创建开销很大的对象。在真实对象创建之前,虚拟代理对象可以充当其代理。例如,在加载大型图片或视频时,可以先显示一个占位符或缩略图,待图片或视频加载完成后再显示真实内容。
  3. 安全代理:控制对资源的访问权限。例如,在Web应用中,可以通过代理对象来检查用户是否具有访问某个资源的权限。
  4. 智能代理:当调用真实对象时,代理对象可以附加一些额外的处理逻辑。例如,在调用数据库查询方法之前,代理对象可以自动添加分页、排序等处理逻辑。

六、优缺点

优点

  1. 中介作用:代理对象可以在客户端和目标对象之间起到中介的作用,从而保护目标对象。
  2. 功能扩展:代理对象可以扩展目标对象的功能,而不需要修改目标对象的代码。
  3. 降低耦合度代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性。

缺点

  1. 类数量增加代理模式会造成系统设计中类的数量增加,从而增加了系统的复杂度。
  2. 请求处理变慢:在客户端和目标对象之间增加一个代理对象,可能会造成请求处理速度变慢。

综上所述,代理模式是一种非常有用的设计模式,它可以在不修改目标对象代码的前提下,实现对目标对象功能的扩展和保护。同时,代理模式也具有一定的缺点,需要在具体应用场景中进行权衡和选择。

在这里插入图片描述


http://www.ppmy.cn/news/1560912.html

相关文章

Koi技术教程-Tauri-第三章 Tauri的搭建环境

1 “你日渐平庸&#xff0c;甘于平庸&#xff0c;将继续平庸。”——《以自己喜欢的方式过一生》 2. “总是有人要赢的&#xff0c;那为什么不能是我呢?”——科比布莱恩特 3. “你那么憎恨那些人&#xff0c;和他们斗了那么久&#xff0c;最终却要变得和他们一样&#xff0c;…

练习题:37

目录 Python题目 题目 题目分析 套接字概念剖析 通信原理分析 服务器 - 客户端连接建立过程&#xff1a; 基于套接字通信的底层机制&#xff1a; 代码实现 基于 TCP 的简单服务器 - 客户端通信示例 服务器端代码&#xff08;tcp_server.py&#xff09; 客户端代码&a…

ES数据管理

ES 数据管理 索引在 Elasticsearch 中的生命周期&#xff1a; 针对一个超大规模的集群&#xff1a; 注意&#xff1a;需要确保集群中至少有一个 data_hot 和 data_content 节点&#xff0c;即使它们是同一个节点&#xff0c;否则新索引将无法被分配。 新创建的索引默认将分配…

Kbuild学习知识点

1.Kbuild本质&#xff1a;一个可扩展、可配置的Makefile框架&#xff0c;递归式Makefile&#xff0c;菜单式配置。 2.Kbuild构成&#xff1a; Makefile:顶层目录下的Makefile.config:内核的配置文件arch/S(ARCH)/Makefile:跟平台架构相关的Makefilescripts/Makefile.*:通用编…

【UE5 C++课程系列笔记】19——通过GConfig读写.ini文件

步骤 1. 新建一个Actor类&#xff0c;这里命名为“INIActor” 2. 新建一个配置文件“Test.ini” 添加一个自定义配置项 3. 接下来我们在“INIActor”类中获取并修改“CustomInt”的值。这里定义一个方法“GetINIVariable” 方法实现如下&#xff0c;其中第16行代码用于构建配…

keepalived详细笔记

一、Keepalived 概述 Keepalived 是一种基于 VRRP&#xff08;Virtual Router Redundancy Protocol&#xff0c;虚拟路由器冗余协议&#xff09;协议实现的高可用解决方案&#xff0c;主要用于服务器的负载均衡和高可用性保障&#xff0c;能够在主服务器出现故障时&#xff0c…

VScode SSH 错误:Got bad result from install script 解決

之前vscode好好的&#xff0c;某天突然连接报错如下 尝试1. 服务器没有断开,ssh可以正常连接 2. 用管理员权限运行vscode&#xff0c;无效 3. 删除服务器上的~/.vscode-server 文件夹&#xff0c;无效 试过很多后&#xff0c;原来很可能是前一天anaconda卸载导致注册表项 步…

MATLAB中binopdf函数用法

目录 语法 说明 示例 计算并绘制二项概率密度函数 binopdf函数的功能是计算二项概率密度函数。 语法 y binopdf(x,n,p) 说明 y binopdf(x,n,p) 使用 n 中对应的试验次数和 p 中每次试验的成功概率&#xff0c;计算 x 中每个值处的二项概率密度函数。 x、n 和 p 可以是…