Java 设计模式(十三):代理模式

article/2025/9/10 16:25:53

参考链接:代理模式-Proxy Pattern

近年来,代购已逐步成为电子商务的一个重要分支。代购简单来说就是找人帮忙购买所需要的商品,代购网站就是其中一种产物,它为消费者提供在线的代购服务,如果看中某国外购物网站上的商品,可以登录代购网站填写代购单并付款,代购网站会帮助进行购买然后通过快递公司将商品发送给消费者。商品代购过程如图所示:
在这里插入图片描述

在软件开发中,也有一种设计模式可以提供与代购网站类似的功能。由于某些原因,客户端不想或不能直接访问一个对象,此时可以通过一个称之为“代理”的第三者来实现间接访问,该方案对应的设计模式被称为代理模式。

1. 模式概述

定义:给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。

代理模式是一种对象结构型模式。在代理模式中引入了一个新的代理对象,代理对象在客户端对象和目标对象之间起到中介的作用,它去掉客户不能看到的内容和服务或者增添客户需要的额外的新服务。

代理模式的结构比较简单,其核心是代理类,为了让客户端能够一致性地对待真实对象和代理对象,在代理模式中引入了抽象层,代理模式结构如图所示:
代理模式结构图

  • Subject(抽象主题角色):它声明真实主题和代理主题的共同接口,使得在任何使用真实主题的地方都可以使用代理主题,客户端通常需要针对抽象主题角色进行编程。
  • Proxy(代理主题角色):它包含对真实主题的引用,从而可以在任何时候操作真实主题对象;在代理主题角色中提供一个与真实主题角色相同的接口,以便在任何时候都可以替代真实主题;代理主题角色还可以控制对真实主题的使用,负责在需要的时候创建和删除真实主题对象,并对真实主题对象的使用加以约束。通常,在代理主题角色中,客户端在调用所引用的真实主题操作之前或之后还需要执行其他操作,而不仅仅是单纯调用真实主题对象中的操作。
  • RealSubject(真实主题角色):它定义代理角色所代表的真实对象,在真实主题角色中实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的操作。

2. 模式实现

代理模式的结构图比较简单,但是在真实的使用和实现过程中要复杂很多,特别是代理类的设计和实现。

抽象主题类声明真实主题类和代理类的公共方法,它可以是接口、抽象类或具体类,客户端针对抽象主题类编程,一致性地对待真实主题和代理主题,典型的抽象主题类代码如下:

public interface Subject {void request();
}

真实主题类继承抽象主题类,提供业务方法的具体实现,其典型代码如下:

public class RealSubject implements Subject {@Overridepublic void request() {System.out.println("request");}
}

代理类也是抽象主题类的子类,它维持一个对真实主题对象的引用,调用在真实主题中实现的业务方法,在调用时可以在原有业务方法的基础上附加一些新的方法来对功能进行扩充或约束,最简单的代理类实现代码如下:

public class ProxySubject implements Subject {private RealSubject realSubject = new RealSubject();@Overridepublic void request() {preRequest();realSubject.request();postRequest();}private void preRequest() {System.out.println("preRequest");}private void postRequest() {System.out.println("postRequest");}
}

在实际开发过程中,代理类的实现比上述代码要复杂很多,代理模式根据其目的和实现方式不同可分为很多种类,其中常用的几种代理模式简要说明如下:
(1) 远程代理(Remote Proxy):为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又称为大使(Ambassador)。
(2) 虚拟代理(Virtual Proxy):如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
(3) 保护代理(Protect Proxy):控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
(4) 缓冲代理(Cache Proxy):为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
(5) 智能引用代理(Smart Reference Proxy):当一个对象被引用时,提供一些额外的操作,例如将对象被调用的次数记录下来等。

3. Java两种动态代理方式

3.1 JDK自带的动态代理

  • java.lang.reflect.Proxy:生成动态代理类和对象;
  • java.lang.reflect.InvocationHandler(处理器接口):可以通过invoke方法实现。

典型代码如下:

public class JdkProxy implements InvocationHandler {private Subject realSubject;public Subject newProxy(Subject realSubject) {// 将目标对象传入进行代理this.realSubject = realSubject;// 返回代理对象return (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("调用代理类");Object obj;if ("request".equals(method.getName())) {obj = method.invoke(realSubject, args);System.out.println("调用request方法");} else {obj = method.invoke(realSubject, args);System.out.println("调用其它方法");}return obj;}
}

3.2 Cglib动态代理

Cglib动态代理是针对代理的类,动态生成一个子类,然后子类覆盖代理类中的方法,如果是private或是final类修饰的方法,则不会被重写。

典型代码如下:

public class CglibProxy implements MethodInterceptor {// CGlib需要代理的目标对象private Subject realSubject;public Object createProxyObject(Subject subject) {this.realSubject = subject;Enhancer enhancer = new Enhancer();enhancer.setSuperclass(subject.getClass());enhancer.setCallback(this);Object proxyObj = enhancer.create();return proxyObj;}@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("调用代理类");Object obj;if ("request".equals(method.getName())) {obj = method.invoke(realSubject, args);System.out.println("调用request方法");} else {obj = method.invoke(realSubject, args);System.out.println("调用其它方法");}return obj;}
}

4. 模式总结

代理模式为对象的间接访问提供了一个解决方案,可以对对象的访问进行控制。代理模式类型较多,其中远程代理、虚拟代理、保护代理等在软件开发中应用非常广泛。

1.主要优点
(1) 能够协调调用者和被调用者,在一定程度上降低系统的耦合度。
(2) 客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源代码,符合开闭原则,系统具有较好的灵活性和可扩展性。

2.主要缺点
(1) 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢,例如保护代理。
(2) 实现代理模式需要额外的工作,而且有些代理模式的实现过程较为复杂,例如远程代理。

3.适用场景
(1) 当客户端对象需要访问远程主机中的对象时可以使用远程代理。
(2) 当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系统开销、缩短运行时间时可以使用虚拟代理,例如一个对象需要很长时间才能完成加载时。
(3) 当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时可以使用缓冲代理。通过使用缓冲代理,系统无须在客户端每一次访问时都重新执行操作,只需直接从临时缓冲区获取操作结果即可。
(4) 当需要控制对一个对象的访问,为不同用户提供不同级别的访问权限时可以使用保护代理。
(5) 当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理。


http://chatgpt.dhexx.cn/article/1JXEEPWA.shtml

相关文章

Java设计模式:观察者模式

一、什么是观察者模式? 又叫做发布-订阅模式,定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。UML结构图如下: 其中涉及到四种角色: 1.抽象目标&a…

初探Java设计模式------观察者模式

前言 最近刚开始学习RxJava,众所周知,Rxjava就是扩展的观察者模式,所以想学习Rxjava,先入手了解一下观察者模式是很有必要的。那么今天就先稍微了解一下什么是观察者模式。 定义 观察者(Observer)模式:是对象的行为模…

java设计模式(3)--观察者模式

(一)观察者模式 观察者模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 该模式有以下角色: (1)抽象主题(Subjec…

Java设计模式之行为型:观察者模式

一、什么是观察者模式: 观察者模式又称为 发布-订阅模式,定义了对象之间一对多依赖关系,当目标对象(被观察者)的状态发生改变时,它的所有依赖者(观察者)都会收到通知。一个观察目标可以对应多个观察者,而这些观察者之间…

java监听设计模式(java观察者设计模式)

今天给大家分享一下观察者设计模式(监听设计模式),该模式在很多主流得框架、源码中使用率非常高。在分享之前先给大家讲一个我们使用手机的一个场景,我们都用过手机,当我们手机来电话的时候,会有各种复杂的…

【十一】设计模式~~~结构型模式~~~代理模式(Java)

【学习难度:★★★☆☆,使用频率:★★★★☆】 6.1. 模式动机 在某些情况下,一个客户不想或者不能直接引用一个对 象,此时可以通过一个称之为“代理”的第三者来实现 间接引用。代理对象可以在客户端和目标对象之间起…

JAVA架构之路(设计模式之观察者模式)

设计模式之观察者模式 定义:对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新 核心原理: 1.被观察者中维护一个观察者对象列表 2.观察者可新增可可移除。 需要角色&#…

程序员百宝箱---搭建自己专属的在线工具集

相信各位开发者/程序员在开发过程中都使用过各种在线工具吧。比如 1 校验或格式化 json 字符串 2 使用 base64 的加密解密,url 的编码解码 urlencode、urldecode 等 3 调试正则表达式是否正确 4 时间戳与格式化日期互相转换 5 文本对比,比较两个代码文件…

如何部署JSP应用到阿里云服务器上(一)

今天讲解一下如何部署JSP应用到阿里云服务器上,我使用的后台服务器是 Tomcat服务器,服务器应用的开发语言是 Java Web ,后台的数据库使用的是MySQL 1 打开阿里云官方网站 https://www.aliyun.com/?utm_mediumtext&utm_sourcebdbrand&am…

开源项目精选推荐-杨小杰工具箱(YoungxjTools)

演示 预览地址: https://tools.yum6.cn/ 程序介绍 01、首页底部友情链接 02、网站公告页面功能 03、内置留言管理功能 04、后台网站信息设置 05、内置smtp发信配置 06、支持两主题的切换 07、关于页面支持留言, 08、程序已集成32种小工具 更多功能 介绍 请搭建 自行测试

C++构造函数之初始化列表

C构造函数之初始化列表 构造函数可以说是对于C每个类来说最重要的组成部分,之前也详细介绍过构造函数的相关知识,今天给构造函数进行补充,主要说明一下构造函数的初始值列表 一、初始化列表的写法 仍然以之前介绍构造函数时使用的学生类来…

C++初始化列表详解

目录: 定义 使用初始化列表的原因 必须使用初始化列表的时候 成员变量的顺序 定义 与其他函数不同,构造函数除了有名字,参数列表和函数体之外,还可以有初始化列表,初始化列表以冒号…

C++类的初始化列表

意义 初始化列表是类中构造函数的一部分,用于实例化类中变量时 赋初值。 需要先了解 构造函数的基本知识。CSDN-构造函数https://blog.csdn.net/weixin_44212838/article/details/124901019?spm1001.2014.3001.5501 用法 在函数头与函数体之间,用一…

详解初始化列表

初始化列表 定义 构造函数有个特殊的初始化方式叫“初始化表达式表”(简称初始化列表)。初始化列表位于函数参数表之后,却在函数体 {} 之前。这说明该表里的初始化工作发生在函数体内的任何代码被执行之前。 Date(int year, int month, in…

C++ 成员初始化列表

这里写目录标题 数据成员是某一个类的对象,且这个类没有默认构造函数数据成员的类型是const或者引用子类初始化父类初始化列表先后顺序使用成员初始化列表的优点 在以下三种情况下需要使用初始化成员列表: 需要初始化的数据成员是某一个类的对象&#xf…

【深入理解】初始化列表

目录 一、什么是初始化列表? 二、初始化列表长啥样? 三、初始化列表的特性 1、由于是在定义阶段进行的初始化,所以,只能在定义阶段进行的初始化类型便只能在初始化列表中进行初始化。 2、初始化顺序不是跟着初始化列表走的&#x…

C++ 初始化列表详解

目录 1.什么是初始化列表 2.什么时候需要使用初始化列表? 3.初始化列表的效率 4.初始化列表的初始化顺序 1.什么是初始化列表 class A { public:A(int value):m_dada(value){}private:int m_dada; }; 如上图,红色圈起来的部分,就是构造函…

【C++】-- 初始化列表

目录 一、用初始化列表初始化对象 1.初始化列表用法 2.初始化列表特性 二、explicit关键字 1.内置类型的隐式转换 2.如何避免单参构造函数初始化发生隐式类型转换 三、匿名对象 1.匿名对象定义 2.匿名对象应用场景 创建一个类对象时,编译器通过调用构造函…

字符串逆序(数组倒序输出)

题目信息: 样例输出 copy edcba 新手导读: 题目描述信息:字符串的倒序输出肯定是字符数组的倒序输出; 输入信息:输入条件是最后字符不为回车;所以应该先用变量存入字符进行判断,再使用这个…

C语言实现字符串倒序

C语言显示字符串倒序 倒序字符串的方法很多&#xff0c;这里给出常见的两种 1.倒序字符串指针法 void reverse_string(char *arr) {int len strlen(arr);char* left arr; //指向头部的指针char* right arr (len - 1); //指向尾部的指针char temp;while(left <…