代理模式 - Proxy Patterns

2024-05-11 04:32
文章标签 模式 patterns 代理 proxy

本文主要是介绍代理模式 - Proxy Patterns,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本篇的父博文是:设计模式 - Design Patterns


一、代理模式的意义

Design Patterns 书中对代理模式的描述是这样的:

Provide a surrogate or placeholder for another object to control access to it.
代理模式为一个对象提供一个代理对象,以控制对这个对象的访问。

代理模式很简单有趣,当然也很重要。往下看就很快明白啦~

二、应用场景

所以,为什么要用代理模式呢?或者说代理模式适用于哪些场景呢?


1. 远程代理人(remote proxy)

试想这样一个场景:你需要引用一个网络中的远程对象(对象在其它计算机JVM中)来完成功能的调用,你该怎么引用?

HelloService helloService = new 这该写啥啊?();

稍微上一点规模的系统都会有多个应用的互相调用的需求。那么,在Java 中这代码我们该如何写呢?

这里写图片描述

没错,最优的代码模板就是用代理模式 的思想去实现。

思路

在本地项目中创建一个远程对象的代理对象 - 作为远程对象的代表,并在代理对象中封装对远程程序的访问/调用细节。此时,代码中不再直接访问远程对象,而是访问这个代理对象。如此,你就如同访问一个本地对象那样访问了远程对象。

下面用一个简单写的订单服务 - OrderService作为例子来实现下。

实现步骤

  1. 首先是一个接口:订单服务,提供了一个方法创建订单。

    /*** 订单服务。*/
    public interface OrderService {/*** 创建订单。* @param orderId 订单ID* @return*/String createOrder(Integer orderId) throws Exception;}
  2. 其次呢,其它程序员在远程主机(127.0.0.1)上实现了此接口,并启动了对外服务

    public class OrderRemoteService implements OrderService {@Overridepublic String createOrder(Integer orderId) throws Exception {return "我是127.0.0.1,你创建订单成功!ID:" + orderId;}}

    启动对外服务

    /*** 启动对外远程服务的main方法类。*/
    public class RomteMain {@SuppressWarnings({ "resource" })public static void main(String[] args) throws Exception {// 实际实现类 - 敲黑板!这里是上面那个实际实现类OrderService orderSevice = new OrderRemoteService();// 下面代码:ServerSocket 监听 client 的Socket 来连接,连上了就根据约定读取client 调用的什么方法,然后转而调用实际实现类,并返回结果ServerSocket server = new ServerSocket(9999);// 监听端口while (true) {final Socket socket = server.accept();new Thread(){@Overridepublic void run() {try {ObjectInputStream input = new ObjectInputStream(socket.getInputStream());// 读方法名String methodName = input.readUTF();// 读参数类型Class<?> paramsType = (Class<?>)input.readObject();// 读参数值Object params = (Object)input.readObject();// 使用反射调用相关方法Method method = orderSevice.getClass().getMethod(methodName, paramsType);Object result = method.invoke(orderSevice, params);// 将结果写回给socket ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());output.writeObject(result);} catch (Exception e) {e.printStackTrace();}}}.start();}}
    }
  3. 然后呢,在我们自己程序里写一个代理类,代理类就作为上面那个实现类在我们本地一个代表

    /*** 我是远程对象的代理人。* <p>代理对象去找幕后主使来完成任务</p>*/
    public class OrderServiceProxy implements OrderService {@SuppressWarnings("resource")@Overridepublic String createOrder(Integer orderId) throws Exception {// 1. 连接远程服务Socket socket = new Socket("127.0.0.1", 9999);ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());// 自定一个写出顺序,远程对象那里也要按这个顺序读output.writeUTF("createOrder");// 写出方法名output.writeObject(Integer.class);// 写出参数类型output.writeObject(orderId);// 写出参数值// 2. 读取返回ObjectInputStream input = new ObjectInputStream(socket.getInputStream());return (String)input.readObject();}
    }
  4. 最后呢,我们程序中要用那个远程对象,我们没法直接引用到它,没关系我们用它的代理人

    public class LocalMain {public static void main(String[] args) throws Exception {// 客户端引用代理对象,而不是实际对象OrderService orderService = new OrderServiceProxy();// 直接调用服务就好System.out.println(orderService.createOrder(9527));}
    }

    以上。

总结
  1. 我们已经简单写了一个代理模式 - 远程代理 Java 程序。

    顺便说下,我们写这个也是个简单的RPC 框架~~

    不过有一定经验的读者会发现,上面代码还是比较挫的,这在Java 中叫所谓的“静态代理”。如果我们要多写几个代理类,多写几个方法,上面代码扩展性就极差了。

    但是,上面只是简单尝试实现了“远程代理”,如果要做一个稍微完善点的RPC ,我们当然需要用到Java 中的“动态代理”。实际上,Java 中之所以有“动态代理”,就是为了解决上面说的那类问题的。不过这里暂且不表了,这个并非主线剧情。

  2. 还有一点,是不是还会有人脑海中会有这样一种思考?这不就是个接口的实现类吗,怎么能叫代理模式呢?

    首先,再看一遍设计模式的概念。

    接下来说:你写一个实现类,是你依赖Java 提供的一种实现Interface的代码形式而写出的代码;而你写一个代理类是参照设计模式的方案和思路写出的代码;它们是两个领域两种完全不同的概念啦。


2. 虚拟代理(virtual proxy)

思路

如果一个对象的创建成本非常高且并不是随时需要它,那么在程序启动时我们就不需要创建此对象,而是仅仅创建此对象的一个代理,让客户端引用这个代理就好了。当客户端实际需要使用这个对象的时候,我们再创建这个对象。

实现步骤

/*** 4. 某个客户程序。*/
public class ClientMain {/** 只给客户端持有一个视频服务的代理类,而不是真实服务类 */private static VideoService videoService = new MP4VideoProxyService();public static void main(String[] args) throws Exception {// 客户端程序启动了System.out.println("程序启动了--------");System.out.println("程序运行很久很久--------");Thread.sleep(1000l);// 很久后才突然被触发,需要执行以下代码System.out.println("N小时候触发了某个事件,我们需要获取视频了--------");String result = videoService.getVideo("长城");System.out.println(result);}
}/*** 1. 视频服务“接口”。*/
interface VideoService {String getVideo(String videoName);
}
/*** 2. 视频服务“真实服务类”。* <p>初始化很消耗资源,很耗时</p>*/
class MP4VideoService implements VideoService {public MP4VideoService() {// 很耗时、耗资源的初始化操作System.out.println("我是很耗资源的真实服务,我被创建了!");}@Overridepublic String getVideo(String videoName) {return "找到视频:[" + videoName + "]并返回";}
}/*** 3. 视频服务一个“代理类”。*/
class MP4VideoProxyService implements VideoService {/** 真实服务类的引用,当真正需要时才给其赋值 */private VideoService mp4VideoService;@Overridepublic String getVideo(String videoName) {// 代理类的getVideo方法if (mp4VideoService == null) {mp4VideoService = new MP4VideoService();}return mp4VideoService.getVideo(videoName);}
}

3. 保护代理(protection proxy)

思路:

使用一个代理对象来控制一个实际对象的访问权限。这种用处就先不写代码了,很好想到,就是保护一个对象,使不同的客户端有不同的访问权限。


4. 智能引用(smart reference)

思路:

智能引用取代了简单的指针引用,在对象被访问时能让我们做一些额外的操作。

实现步骤:

这个最大名鼎鼎的应用就是AOP 了吧。
正好,接下来我使用上面没有使用的,Java动态代理 的方式来实现一个AOP 代码。

  1. 首先,我们创建一个interfaceCalculateService ,用来表示计算服务,并定义加、减、乘和除四个方法

    public interface CalculateService {int calculateAdd(int a, int b);int calculateSubtract(int a, int b);int calculateMultiply(int a, int b);int calculateDivide(int a, int b);
    }
  2. 然后,我们用一个类来实现这个接口的方法,这个类就是作为一个真是实现类

    public class CalculateServiceImpl implements CalculateService {public int calculateAdd(int a, int b) {return a + b;}public int calculateSubtract(int a, int b) {return a - b;}public int calculateMultiply(int a, int b) {return a * b;}public int calculateDivide(int a, int b) {return a / b;}}
  3. 最后,我们用动态代理的API,来实现AOP ,我们统计下耗费的时间。

    public class MainAOP {// 现在我们有一个服务CalculateService,它提供了基本的加减乘除。// * 突然的,你多了个需求,想在执行加减乘除方法时,都计算一下它执行所耗费的时间。// ** 咋办?去改CalculateServiceImpl吗?有时候你没有权限改这个/你不应该侵入人家的代码/其它调用这个服务的人可能并不需要计时// ** 那代码怎么写能很好的解决这个问题呢?// *** 灯~灯~灯~灯…… 所以你学了设计模式,你想到“可以用aop的思想 + JavaSE中提供的Proxy等类的功能”来写代码。开始!/** 计算服务 */private static CalculateService calculateService = getCalculateService();/************************************ 模拟使用↓ **************************************/public static void main(String[] args) {// 牛!calculateService.calculateAdd(1, 1);}/*** 获得计算服务。<br>* 你利用你的aop思想,将代码写成了下面这样。<br>* 你不费吹灰之力,没有写代理类.java的情况下,把功能给完成了。还对原代码无侵入,还能随意判断,以根据不同的方法去决定是否要计算时间。牛!* @return*/private static CalculateService getCalculateService() {return (CalculateService) Proxy.newProxyInstance(MainAOP.class.getClassLoader(), new Class[] { CalculateService.class }, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {long startTime = System.currentTimeMillis();// 核心调用实现类Object result = method.invoke(new CalculateServiceImpl(), args);long endTime = System.currentTimeMillis();// 此次aop的目的:输出耗时System.out.println("耗时:" + (endTime - startTime));return result;}});}
    }

    需要解释的点,代码中的注释都写上啦。


以上我们了解了:设计模式之 > 结构模式之 > 代理模式

请持续关注我的博客哈,会继续推出设计模式系列。


转载注明出处:http://blog.csdn.net/u010297957/article/details/53885454

这篇关于代理模式 - Proxy Patterns的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/978504

相关文章

Java实现状态模式的示例代码

《Java实现状态模式的示例代码》状态模式是一种行为型设计模式,允许对象根据其内部状态改变行为,本文主要介绍了Java实现状态模式的示例代码,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来... 目录一、简介1、定义2、状态模式的结构二、Java实现案例1、电灯开关状态案例2、番茄工作法状态案例

高效+灵活,万博智云全球发布AWS无代理跨云容灾方案!

摘要 近日,万博智云推出了基于AWS的无代理跨云容灾解决方案,并与拉丁美洲,中东,亚洲的合作伙伴面向全球开展了联合发布。这一方案以AWS应用环境为基础,将HyperBDR平台的高效、灵活和成本效益优势与无代理功能相结合,为全球企业带来实现了更便捷、经济的数据保护。 一、全球联合发布 9月2日,万博智云CEO Michael Wong在线上平台发布AWS无代理跨云容灾解决方案的阐述视频,介绍了

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

模版方法模式template method

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/template-method 超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。 上层接口有默认实现的方法和子类需要自己实现的方法

【iOS】MVC模式

MVC模式 MVC模式MVC模式demo MVC模式 MVC模式全称为model(模型)view(视图)controller(控制器),他分为三个不同的层分别负责不同的职责。 View:该层用于存放视图,该层中我们可以对页面及控件进行布局。Model:模型一般都拥有很好的可复用性,在该层中,我们可以统一管理一些数据。Controlller:该层充当一个CPU的功能,即该应用程序

迭代器模式iterator

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/iterator 不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素

《x86汇编语言:从实模式到保护模式》视频来了

《x86汇编语言:从实模式到保护模式》视频来了 很多朋友留言,说我的专栏《x86汇编语言:从实模式到保护模式》写得很详细,还有的朋友希望我能写得更细,最好是覆盖全书的所有章节。 毕竟我不是作者,只有作者的解读才是最权威的。 当初我学习这本书的时候,只能靠自己摸索,网上搜不到什么好资源。 如果你正在学这本书或者汇编语言,那你有福气了。 本书作者李忠老师,以此书为蓝本,录制了全套视频。 试

利用命令模式构建高效的手游后端架构

在现代手游开发中,后端架构的设计对于支持高并发、快速迭代和复杂游戏逻辑至关重要。命令模式作为一种行为设计模式,可以有效地解耦请求的发起者与接收者,提升系统的可维护性和扩展性。本文将深入探讨如何利用命令模式构建一个强大且灵活的手游后端架构。 1. 命令模式的概念与优势 命令模式通过将请求封装为对象,使得请求的发起者和接收者之间的耦合度降低。这种模式的主要优势包括: 解耦请求发起者与处理者

springboot实战学习(1)(开发模式与环境)

目录 一、实战学习的引言 (1)前后端的大致学习模块 (2)后端 (3)前端 二、开发模式 一、实战学习的引言 (1)前后端的大致学习模块 (2)后端 Validation:做参数校验Mybatis:做数据库的操作Redis:做缓存Junit:单元测试项目部署:springboot项目部署相关的知识 (3)前端 Vite:Vue项目的脚手架Router:路由Pina:状态管理Eleme

状态模式state

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/state 在一个对象的内部状态变化时改变其行为, 使其看上去就像改变了自身所属的类一样。 在状态模式中,player.getState()获取的是player的当前状态,通常是一个实现了状态接口的对象。 onPlay()是状态模式中定义的一个方法,不同状态下(例如“正在播放”、“暂停