news 2026/6/23 11:53:24

GoF设计模式——代理模式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
GoF设计模式——代理模式

为什么需要代理模式?

有时候我们不能或不想直接访问某个对象。比如对象创建开销很大需要延迟加载,或者需要在访问前做权限检查,或者需要记录访问日志。直接在业务代码中掺杂这些逻辑会让代码臃肿且难以维护。

代理模式通过引入一个中间层,将这些控制逻辑从业务代码中分离出来。客户端代码不需要知道它是在和代理交互还是真实对象交互,两者可以透明替换。

概念

代理模式Proxy Pattern是一种结构型设计模式,核心思想是为一个对象提供一个替身(代理),以控制对这个对象的访问

代理对象和真实对象实现相同的接口,客户端通过代理间接访问真实对象,代理可以在调用前后添加额外的控制逻辑。

代理模式的主要角色有:

  • Subject(抽象主题):声明真实主题和代理共同实现的业务方法,客户端面向该接口编程
  • RealSubject(真实主题):定义代理所代表的真实对象,是客户端最终要访问的对象
  • Proxy(代理):持有对真实主题的引用,实现与真实主题相同的接口,在调用真实主题前后添加控制逻辑

类图展示了静态结构,但对代理模式这种"调用拦截"的场景,时序图更能体现动态调用流程:

RealSubjectProxyClientRealSubjectProxyClientrequest()前置处理(权限/日志/延迟加载)request()返回结果后置处理(日志/缓存)返回结果

可以把代理理解为私人助理:老板(客户端)有事找某人(真实对象),先通过助理(代理)。助理可以在前面挡掉不重要的打扰(权限控制),也可以在事后记录行程(日志),老板全程不需要直接接触对方。这个比喻贯穿后面的实现章节,方便对照理解。

实现

代理模式的基本实现分为以下几个步骤:

  1. 定义抽象主题,一般是接口或抽象类,声明真实主题和代理对象实现的业务方法
  2. 定义真实主题,实现抽象主题中的具体业务
  3. 定义代理类,包含对RealSubject的引用,提供和真实主题相同的接口,在调用前后添加控制逻辑
  4. 客户端使用代理
// 抽象主题 interface Subject { // 声明同业务对象同名的方法 public void request(); } // 真实主题 class RealSubject implements Subject { public void request() { System.out.println("RealSubject request"); } } // 代理类 class Proxy implements Subject { private RealSubject realSubject; @Override public void request() { // 访问真实主题之前:延迟加载 if (realSubject == null) { realSubject = new RealSubject(); } // 调用真实主题的方法 realSubject.request(); // 访问真实主题之后:可添加日志等逻辑 } }

总结

代理模式本质上是一层"中间人"——为真实对象提供一个替身,在调用前后添加控制逻辑。

什么时候用

  • 想控制对某个对象的访问(权限、延迟加载、缓存、日志)
  • 需要为远程对象提供本地代表
  • 引入第三方库或遗留代码,需要统一调用方式

什么时候不用

  • 接口差异巨大,代理会变得臃肿
  • 能修改真实对象源码且代价不大,直接修改更简单
  • 系统设计阶段就能定义接口规范,从源头统一即可

简单记忆

代理解决"控制访问"的问题,是给真实对象"加一层控制"。能改源码就改,改不了才用代理。

代理 vs 装饰器 vs 适配器 vs 外观:四个结构型模式都"包了一层对象",结构相似但意图不同:

模式接口关系核心意图
代理目标接口 = 被包装对象接口控制访问,附加访问前后逻辑
装饰器目标接口 = 被包装对象接口增强功能,接口不变
适配器目标接口 ≠ 被包装对象接口转换接口,让不兼容的类协同
外观目标接口是新设计的简化复杂子系统的调用

口诀对比:代理控访问,装饰增功能,适配改接口,外观简调用。

代理模式 vs 中介者模式

两者都引入"中间层",结构相似,但意图完全不同:

维度代理模式中介者模式
核心意图控制对单个对象的访问协调多个对象之间的交互
对象关系客户端 → 代理 → 真实对象(单向委托)多个同事对象 ↔ 中介者 ↔ 多个同事对象(多向协调)
封装内容访问控制逻辑(权限、延迟加载、缓存)对象间的交互规则、通信协议
客户端感知客户端不知道真实对象存在各同事对象知道中介者存在,但不直接知道其他同事
应用场景Spring AOP、MyBatis Mapper、远程调用MVC 框架的 Controller、聊天室服务器、GUI 事件分发

用例子说明

  • 代理模式:你要见 CEO,先通过秘书(代理)。秘书控制访问——过滤不重要的人、安排时间。你只和秘书打交道,CEO 对你透明。
  • 中介者模式:公司的各部门(销售、研发、财务)不直接相互沟通,所有协调通过行政部(中介者)。销售要研发资源,找行政部安排;财务要销售数据,找行政部转发。各部门知道行政部,但不直接依赖其他部门。

简单记忆

代理管"谁能动",中介者管"怎么联动"。代理是单对象的门禁,中介者是多对象的调度中心。

常见误区

  • 误区:代理模式 = 装饰器模式 → 意图不同:一个控访问,一个增功能
  • 误区:代理必须和真实对象同接口 → 对,这是代理的基本要求,否则就不是代理了
  • 误区:Spring@Transactional在同一个类内部调用不生效 → 这是自调用绕过代理的经典坑,AOP 代理需要外部调用才能触发拦截逻辑

练习题目

门禁系统权限控制

题目描述:某科技园区的门禁系统管理着多个房间,每个房间有名称和最低访问权限等级。用户通过门禁终端访问房间,门禁终端作为代理,会检查用户的权限等级:

  • 用户权限 ≥ 房间要求的等级 → 放行,房间显示欢迎信息
  • 用户权限 < 房间要求的等级 → 拒绝,房间不会响应

请使用代理模式实现该门禁系统。其中:

  • Room(真实对象)拥有 enter() 方法,输出欢迎信息
  • RoomProxy(代理)在调用 enter() 前进行权限检查,只有通过才委托给真实对象

输入描述:第一行输入一个整数,表示用户的权限等级。第二行输入一个整数 N(1 ≤ N ≤ 20),表示要访问的房间数量。接下来 N 行,每行包含房间名称和该房间要求的最低权限等级,用空格分隔。

输出描述:对每个房间,通过代理访问后输出:

  • 权限足够:欢迎进入[房间名]
  • 权限不足:权限不足,无法进入[房间名]

输入示例

2 3 MeetingRoom 1 Lab 3 Office 2

输出示例

欢迎进入MeetingRoom 权限不足,无法进入Lab 欢迎进入Office

解题思路:代理类RoomProxy实现Room接口,内部持有用户权限。在enter()方法中先检查用户权限是否满足房间要求,满足则委托给RealRoom.enter(),否则直接拒绝。这体现了代理模式的核心——代理在调用真实对象之前增加控制逻辑(权限校验),对客户端透明。

import java.util.*; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int qx = sc.nextInt(); int n = sc.nextInt(); RoomProxy proxy = new RoomProxy(qx); while (n-- > 0) { String name = sc.next(); int roomQx = sc.nextInt(); RealRoom room = new RealRoom(name, roomQx); proxy.setRoom(room); proxy.enter(); } } } interface Room { public void enter(); } class RealRoom implements Room { private String name; private int qx; public RealRoom(String name, int qx) { this.name = name; this.qx = qx; } public void enter() { System.out.println("欢迎进入" + this.name); } public String getName() { return this.name; } public int getQx() { return this.qx; } } class RoomProxy implements Room { private int qx; private RealRoom room; public RoomProxy(int qx) { this.qx = qx; } public void setRoom(RealRoom room) { this.room = room; } public void enter() { if (this.qx >= room.getQx()) { room.enter(); } else { System.out.println("权限不足,无法进入" + room.getName()); } } }

扩展:实际项目中的代理模式

Spring AOP 的动态代理

Spring AOP 是代理模式最经典的应用。当 Bean 被 AOP 增强时,Spring 不会返回原始对象,而是返回一个代理对象。代理在方法调用前后插入切面逻辑(事务、日志、权限校验等),对调用方完全透明。

// 业务代码:完全感知不到代理的存在 @Service public class OrderService { @Transactional // 事务由代理自动管理 public void createOrder(Order order) { orderDao.save(order); inventoryService.deduct(order.getProductId(), order.getQty()); } } // Spring 内部:创建代理对象(JDK 动态代理或 CGLIB) // 代理在 createOrder 前后自动开启/提交/回滚事务

关键点:Spring 默认对实现了接口的 Bean 使用 JDK 动态代理,对没有实现接口的 Bean 使用 CGLIB 代理。开发者只需写@Transactional@Cacheable等注解,代理负责增强逻辑的织入。

JDK 动态代理 vs CGLIB 对比

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

低成本创业辅助软件客观梳理

低成本创业辅助软件客观梳理开篇一人创业、副业运营或是独立开发时&#xff0c;多数人会陷入工具割裂的困扰&#xff1a;赛道调研、内容产出、产品开发、客户记录分散在不同软件&#xff0c;推进完整项目需要反复复制搬运文件&#xff0c;阶段性复盘资料无法统一留存&#xff1…

作者头像 李华
网站建设 2026/6/23 11:45:46

两个关于数据库的简单项目系统

这里分享我在大学数据库课程的两个项目&#xff0c;事先声明这两个均为转载内容&#xff1a;第一个是图书管理系统&#xff0c;编程语言为Python&#xff0c;由我数据库老师编写&#xff1b;第二个是长途驱车系统&#xff0c;编程语言为Jave。均附带研究报告哦

作者头像 李华
网站建设 2026/6/23 11:41:17

TrueAsync Server 为 PHP 带来了原生的高性能 HTTP 服务器

一切都在一个线程中首先也是最重要的一点&#xff1a;TrueAsync Server 是一个"一切都在一个线程中"的服务器。从解析请求到发送响应的整个处理过程&#xff0c;都在单一线程上完成。在这一点上&#xff0c;TrueAsync Server 在以非 PHP 语言实现的 PHP 生态项目中几…

作者头像 李华
网站建设 2026/6/23 11:27:02

从618「称冠」到世界杯「裁判」,海信RGB-Mini LED如何霸屏赛场内外?

【潮汐商业评论/文】北京时间6月17日&#xff0c;阿根廷迎来2026世界杯首秀。38岁的梅西用一记“帽子戏法”&#xff0c;将全球目光锁定在这片绿茵场上。三粒进球&#xff0c;三种方式&#xff1a;禁区外远射世界波、机敏补射破门、弧顶推射。当球入网的瞬间&#xff0c;转播镜…

作者头像 李华
网站建设 2026/6/23 11:21:54

AI Agent 常见架构全景

一、ReAct 架构&#xff08;Reasoning Acting&#xff09;最经典、应用最广泛的 Agent 架构&#xff0c;OpenAI Function Calling / LangChain 的底层逻辑都基于此。执行流程┌──────────────────────────────────────────────…

作者头像 李华
网站建设 2026/6/23 11:21:48

【2026奇点大会独家授权】:AI状态跟踪的“因果一致性”新标准正式发布,附GitHub开源参考实现(限前500名)

更多请点击&#xff1a; https://kaifayun.com 第一章&#xff1a;AI原生状态管理&#xff1a;2026奇点智能技术大会对话状态跟踪 在2026奇点智能技术大会上&#xff0c;AI原生状态管理成为核心议题——系统不再将对话状态视为临时缓存或外部数据库记录&#xff0c;而是作为模…

作者头像 李华