news 2026/5/8 12:22:55

(31)GoF之代理模式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
(31)GoF之代理模式

对代理模式的理解

生活场景1:牛村的牛二看上了隔壁村小花,牛二不好意思直接找小花,于是牛二找来了媒婆王妈妈 。这里面就有一个非常典型的代理模式。牛二不能和小花直接对接,只能找一个中间人。其中王妈妈是代理类,牛二是目标类。王妈妈代替牛二和小花先见个面。(现实生活中的婚介所)【在程序中,对象A和对象B无法直接交互时。】
生活场景2:你刚到北京,要租房子,可以自己找,也可以找链家帮你找。其中链家是代理类,你是目标类。你们两个都有共同的行为:找房子。不过链家除了满足你找房子,另外会收取一些费用的。(现实生活中的房产中介)【在程序中,功能需要增强时。】
西游记场景:八戒和高小姐的故事。八戒要强抢民女高翠兰。悟空得知此事之后怎么做的?悟空幻化成高小姐的模样。代替高小姐与八戒会面。其中八戒是客户端程序。悟空是代理类。高小姐是目标类。那天夜里,在八戒眼里,眼前的就是高小姐,对于八戒来说,他是不知道眼前的高小姐是悟空幻化的,在他内心里这就是高小姐。所以悟空代替高小姐和八戒亲了嘴儿。这是非常典型的代理模式实现的保护机制。代理模式中有一个非常重要的特点:对于客户端程序来说,使用代理对象时就像在使用目标对象一样。【在程序中,目标需要被保护时】
业务场景:系统中有A、B、C三个模块,使用这些模块的前提是需要用户登录,也就是说在A模块中要编写判断登录的代码,B模块中也要编写,C模块中还要编写,这些判断登录的代码反复出现,显然代码没有得到复用,可以为A、B、C三个模块提供一个代理,在代理当中写一次登录判断即可。代理的逻辑是:请求来了之后,判断用户是否登录了,如果已经登录了,则执行对应的目标,如果没有登录则跳转到登录页面。【在程序中,目标不但受到保护,并且代码也得到了复用。】

代理模式是GoF23种设计模式之一。属于结构型设计模式。

代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用。代理对象可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户不应该看到的内容和服务或者添加客户需要的额外服务。 通过引入一个新的对象来实现对真实对象的操作或者将新的对象作为真实对象的一个替身,这种实现机制即为代理模式,通过引入代理对象来间接访问一个对象,这就是代理模式的模式动机。
代理模式中的角色:

  • 代理类(代理主题)

  • 目标类(真实主题)

  • 代理类和目标类的公共接口(抽象主题):客户端在使用代理类时就像在使用目标类,不被客户端所察觉,所以代理类和目标类要有共同的行为,也就是实现共同的接口。
    代理模式在代码实现上,包括两种形式:

  • 静态代理

  • 动态代理

静态代理

现在有这样一个接口和实现类:

packagecom.powernode.mall.service;/** * 订单接口 * @author 动力节点 * @version 1.0 * @className OrderService * @since 1.0 **/publicinterfaceOrderService{/** * 生成订单 */voidgenerate();/** * 查看订单详情 */voiddetail();/** * 修改订单 */voidmodify();}
packagecom.powernode.mall.service.impl;importcom.powernode.mall.service.OrderService;/** * @author 动力节点 * @version 1.0 * @className OrderServiceImpl * @since 1.0 **/publicclassOrderServiceImplimplementsOrderService{@Overridepublicvoidgenerate(){try{Thread.sleep(1234);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("订单已生成");}@Overridepublicvoiddetail(){try{Thread.sleep(2541);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("订单信息如下:******");}@Overridepublicvoidmodify(){try{Thread.sleep(1010);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("订单已修改");}}

其中Thread.sleep()方法的调用是为了模拟操作耗时。
项目已上线,并且运行正常,只是客户反馈系统有一些地方运行较慢,要求项目组对系统进行优化。于是项目负责人就下达了这个需求。首先需要搞清楚是哪些业务方法耗时较长,于是让我们统计每个业务方法所耗费的时长。如果是你,你该怎么做呢?
第一种方案:直接修改Java源代码,在每个业务方法中添加统计逻辑,如下:

packagecom.powernode.mall.service.impl;importcom.powernode.mall.service.OrderService;/** * @author 动力节点 * @version 1.0 * @className OrderServiceImpl * @since 1.0 **/publicclassOrderServiceImplimplementsOrderService{@Overridepublicvoidgenerate(){longbegin=System.currentTimeMillis();try{Thread.sleep(1234);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("订单已生成");longend=System.currentTimeMillis();System.out.println("耗费时长"+(end-begin)+"毫秒");}@Overridepublicvoiddetail(){longbegin=System.currentTimeMillis();try{Thread.sleep(2541);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("订单信息如下:******");longend=System.currentTimeMillis();System.out.println("耗费时长"+(end-begin)+"毫秒");}@Overridepublicvoidmodify(){longbegin=System.currentTimeMillis();try{Thread.sleep(1010);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("订单已修改");longend=System.currentTimeMillis();System.out.println("耗费时长"+(end-begin)+"毫秒");}}

需求可以满足,但显然是违背了OCP开闭原则。这种方案不可取。
第二种方案:编写一个子类继承OrderServiceImpl,在子类中重写每个方法,代码如下:

packagecom.powernode.mall.service.impl;/** * @author 动力节点 * @version 1.0 * @className OrderServiceImplSub * @since 1.0 **/publicclassOrderServiceImplSubextendsOrderServiceImpl{@Overridepublicvoidgenerate(){longbegin=System.currentTimeMillis();super.generate();longend=System.currentTimeMillis();System.out.println("耗时"+(end-begin)+"毫秒");}@Overridepublicvoiddetail(){longbegin=System.currentTimeMillis();super.detail();longend=System.currentTimeMillis();System.out.println("耗时"+(end-begin)+"毫秒");}@Overridepublicvoidmodify(){longbegin=System.currentTimeMillis();super.modify();longend=System.currentTimeMillis();System.out.println("耗时"+(end-begin)+"毫秒");}}

这种方式可以解决,但是存在两个问题:

  • 第一个问题:假设系统中有100个这样的业务类,需要提供100个子类,并且之前写好的创建Service对象的代码,都要修改为创建子类对象。
  • 第二个问题:由于采用了继承的方式,导致代码之间的耦合度较高。

这种方案也不可取。
第三种方案:使用代理模式(这里采用静态代理)
可以为OrderService接口提供一个代理类。

packagecom.powernode.mall.service;/** * @author 动力节点 * @version 1.0 * @className OrderServiceProxy * @since 1.0 **/publicclassOrderServiceProxyimplementsOrderService{// 代理对象// 目标对象privateOrderServiceorderService;// 通过构造方法将目标对象传递给代理对象publicOrderServiceProxy(OrderServiceorderService){this.orderService=orderService;}@Overridepublicvoidgenerate(){longbegin=System.currentTimeMillis();// 执行目标对象的目标方法orderService.generate();longend=System.currentTimeMillis();System.out.println("耗时"+(end-begin)+"毫秒");}@Overridepublicvoiddetail(){longbegin=System.currentTimeMillis();// 执行目标对象的目标方法orderService.detail();longend=System.currentTimeMillis();System.out.println("耗时"+(end-begin)+"毫秒");}@Overridepublicvoidmodify(){longbegin=System.currentTimeMillis();// 执行目标对象的目标方法orderService.modify();longend=System.currentTimeMillis();System.out.println("耗时"+(end-begin)+"毫秒");}}

这种方式的优点:符合OCP开闭原则,同时采用的是关联关系,所以程序的耦合度较低。所以这种方案是被推荐的。
编写客户端程序:

packagecom.powernode.mall;importcom.powernode.mall.service.OrderService;importcom.powernode.mall.service.OrderServiceProxy;importcom.powernode.mall.service.impl.OrderServiceImpl;/** * @author 动力节点 * @version 1.0 * @className Client * @since 1.0 **/publicclassClient{publicstaticvoidmain(String[]args){// 创建目标对象OrderServicetarget=newOrderServiceImpl();// 创建代理对象OrderServiceproxy=newOrderServiceProxy(target);// 调用代理对象的代理方法proxy.generate();proxy.modify();proxy.detail();}}

运行结果:

以上就是代理模式中的静态代理,其中OrderService接口是代理类和目标类的共同接口。OrderServiceImpl是目标类。OrderServiceProxy是代理类。
大家思考一下:如果系统中业务接口很多,一个接口对应一个代理类,显然也是不合理的,会导致类爆炸。怎么解决这个问题?动态代理可以解决。因为在动态代理中可以在内存中动态的为我们生成代理类的字节码。代理类不需要我们写了。类爆炸解决了,而且代码只需要写一次,代码也会得到复用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/4 23:16:40

低代码平台怎么选?这5款免费方案很友好

概述这两年不管是做业务系统、内部工具,还是想推进企业数字化,低代码平台真的帮了大忙。实际用下来会发现,选对平台,很多原本要排期开发的事情,用配置就能解决,而且成本压力也小很多。这里结合自己和身边朋…

作者头像 李华
网站建设 2026/5/6 10:47:11

HTML DOM 元素

HTML DOM 元素 HTML DOM(文档对象模型)是现代网页设计的基础。DOM 将 HTML 文档解析为一个树状结构,使得开发者能够通过编程方式操作网页内容、样式和行为。本文将深入探讨 HTML DOM 元素的概念、结构和用途。 概念 DOM(Document Object Model)是一种跨平台和语言独立的…

作者头像 李华
网站建设 2026/5/5 21:17:51

基于springboot+vue的微信小程序的公开课管理系统(源码+lw+部署文档+讲解等)

课题介绍随着在线教育的蓬勃发展,公开课因优质教育资源共享的特性备受青睐,但传统公开课管理模式存在课程信息分散、报名流程繁琐、学习进度难追踪、师生互动不便捷等痛点,影响教学效果与管理效率。本课题聚焦公开课教学管理场景,…

作者头像 李华
网站建设 2026/5/2 12:17:49

【配送路径规划】基于雪橇犬算法SDO求解带时间窗的骑手外卖配送路径规划问题(目标函数:最优路径成本 含服务客户数量 服务时间 载量 路径长度)附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室🍊个人信条:格物致知,完整Matlab代码获取及仿真…

作者头像 李华
网站建设 2026/5/5 13:56:35

挑企业微信 SCRM 迷茫?从私域转化需求切入,微伴助手凭什么是第一选择

现在用企业微信的老板越来越多,但不少人觉得 “不好用”—— 加客户慢、发消息没效果、员工离职带跑客户,问题一堆。其实不是企业微信本身不行,而是缺了合适的 “企业微信工具” 来补位。这些工具就像 “外挂”,能把企业微信的基础…

作者头像 李华
网站建设 2026/5/5 15:33:33

AI时代的技术债务管理:新工具与新方法

AI时代的技术债务管理:新工具与新方法关键词:AI时代、技术债务管理、新工具、新方法、软件开发摘要:在AI时代,软件开发面临着诸多新的挑战和机遇,技术债务管理也变得尤为重要。本文深入探讨了AI时代技术债务管理的相关内容&#x…

作者头像 李华