🎯 导读:代理模式是一个非常实用、在底层开发中随处可见的结构型设计模式。它的核心思想用一句话概括:**”不要直接碰那个真实的对象,找个替身(中介)来帮你办事。”** 本文配有 📊 UML类图 和 💻 完整代码案例,带你彻底掌握这个”替身”技巧!
🎭 1. 什么是代理模式?
代理模式(Proxy Pattern) 是一个非常实用、而且在底层开发中随处可见的结构型设计模式。
它的核心思想用一句大白话来说就是:
“不要直接碰那个真实的对象,找个替身(中介)来帮你办事。”
代理对象拥有和真实对象完全相同的接口(看起来一模一样),但它会在你真正调用真实对象的方法之前,偷偷帮你做一些”额外的控制”——比如:
| 控制类型 | 作用 |
|---|---|
| ⏱️ 延迟加载 | 省内存,用到时才加载 |
| 🔒 权限校验 | 保安全,检查用户权限 |
| 📝 记录日志 | 追踪调用过程 |
| 💾 缓存结果 | 避免重复计算 |
🏗️ 2. 生动的实战场景:大型网格模型的”替身”
在开发复杂的工业软件(例如拓扑优化系统)时,我们经常要解析外部的 CAD 模型或网格文件(比如 Gmsh 生成的 .msh 文件)。
❌ 痛点(没有代理模式)
假设用户导入了一个 5GB 大小的超精细 Gmsh 网格文件。如果系统在创建 MeshModel 对象的那一瞬间,就直接去硬盘里把这 5GB 数据全部读进内存,那软件界面会直接卡死好几分钟!
1 | 用户拖拽文件 → 系统立即读取 5GB → 界面卡死 120 秒 → 用户崩溃 💥 |
问题:很多时候,用户导入模型只是想先看看项目结构,暂时并不想立即渲染或进行有限元求解!
✅ 解法(虚拟代理模式 - Virtual Proxy)
我们给真实的网格模型找一个替身(Proxy):
替身非常轻量级,只保存文件路径等少量信息。当用户导入文件时,我们立刻把这个“替身”交给用户。只有当用户真正点击了“开启 3D 渲染”或者“开始求解”,调用了核心计算方法时,替身才会在内部触发真实网格的加载。
1 | 用户拖拽文件 → 创建轻量级替身(瞬间完成)→ 界面秒开 ✅ |
这就叫延迟加载(Lazy Loading)!
💡 补充说明:这里的“偷偷加载”并不一定意味着“后台异步加载”。在本文代码里,它只是把真正的加载时机推迟到了第一次使用真实能力时。
🧩 3. 客户端需要知道哪些类?
先说一个很容易混淆的问题:客户端到底要知道哪些类?
在本文这套写法里,作为在 main 函数里写代码的客户端程序员:
✅ 你通常至少需要知道:
- 🎯 抽象接口(Subject Interface) —— 因为你要用它来定义变量、调用方法。
- 🎭 代理类(Proxy Class) —— 因为本文示例里是由客户端直接创建代理对象。
❌ 你不需要/不应该知道的类:
- 🔒 真实目标类(Real Subject) —— 业务层通常不应该直接依赖它。客户端以为自己在使用“网格模型接口”,但实际一直在和代理打交道,真实类的细节被代理隐藏起来了。
📌 再补一句:如果代理对象不是由客户端手动
new,而是由工厂、IOC 容器或其他装配代码注入,那么客户端甚至可能只需要知道抽象接口。
📊 4. UML 类图详解
看完场景故事,我们来看看代理模式的结构:
classDiagram
class Client {
+ main()
}
class IGmshModel {
+ render3D()
+ getNodeCount()
}
class GmshProxy {
- filePath
- realModel
+ render3D()
+ getNodeCount()
- lazyLoad()
}
class RealGmshModel {
- filePath
+ render3D()
+ getNodeCount()
}
Client ..> IGmshModel : 依赖接口
Client ..> GmshProxy : 创建代理
GmshProxy ..> RealGmshModel : 延迟创建
IGmshModel <|.. GmshProxy : 实现
IGmshModel <|.. RealGmshModel : 实现
🔑 三大核心角色
| 角色 | 类名 | 职责 | 比喻 |
|---|---|---|---|
| 🎯 抽象接口 | IGmshModel |
定义真实对象和代理的共同接口 | 网格模型的标准规格 |
| 🔒 真实目标类 | RealGmshModel |
真正干活的对象,又重又慢 | 5GB 的真实网格数据 |
| 🎭 代理类 | GmshProxy |
轻量级替身,控制对真实对象的访问 | 网格模型的”经纪人” |
💻 5. C++ 现代代码生动演绎
🎯 智能指针版(推荐)
1 |
|
📤 输出结果
1 | === 1. 用户拖拽导入了大型模型文件 === |
🎨 6. 对象结构图解
1 | 初始状态(用户刚导入文件): |
📋 7. 代理模式的变种与工业用途
在上面的例子中,我们实现的是**”虚拟代理(Virtual Proxy)”**,也就是为了延迟加载。除了这个,代理模式还有几个极其厉害的亲戚:
1. 🛡️ 保护代理(Protection Proxy)
用来做权限控制。客户端调用 proxy->deleteProject() 时:
- 代理先检查当前登录的用户是不是管理员
- 是 → 转发给真实对象
- 否 → 直接报错
classDiagram
class User {
+ role
}
class ProjectProxy {
+ deleteProject()
- checkPermission()
}
class RealProject {
+ deleteProject()
}
User ..> ProjectProxy : 调用
ProjectProxy ..> RealProject : 有权限时转发
2. 🌐 远程代理(Remote Proxy)
真实对象根本不在你的电脑上,而在云端服务器里(比如 RPC 远程过程调用)。
你调用的代理方法,其实是代理把请求打包成网络数据发给了服务器。
1 | 客户端 → 远程代理 → 网络请求 → 服务器 → 真实对象 |
3. 💾 缓存代理(Cache Proxy)
如果真实对象计算某个结果(比如高精度有限元求解)要很久,代理可以把上次算过的结果存起来。
下次再调同一个参数,代理直接把缓存返回,根本不去惊动真实对象。
classDiagram
class CalculationProxy {
- cache
+ calculate(params)
- checkCache(params)
}
class RealCalculator {
+ calculate(params)
}
CalculationProxy ..> RealCalculator : 缓存未命中时
🎯 8. 代理模式 vs 装饰模式
很多初学者觉得代理模式和装饰模式长得一模一样(都是一个类包着另一个同类型的类),但它们的意图完全不同:
classDiagram
class Client {
+ main()
}
class Subject {
+ operation()
}
class Proxy {
+ operation()
- controlAccess()
}
class RealSubject {
+ operation()
}
class Component {
+ operation()
}
class Decorator {
+ operation()
- addBehavior()
}
class ConcreteComponent {
+ operation()
}
Client ..> Proxy : 控制访问
Client ..> Decorator : 增强功能
Proxy ..> RealSubject : 保护/延迟
Decorator ..> ConcreteComponent : 包装
| 对比项 | 代理模式 | 装饰模式 |
|---|---|---|
| 🎯 目的 | “设关卡” —— 控制对真实对象的访问 | “加特技” —— 丰富功能 |
| 🔒 客户端与真实对象的关系 | 通常不直接依赖真实对象,更关注“访问是否被控制” | 通常由客户端或装配代码主动包装组件,更关注“功能是否被增强” |
| 📝 典型场景 | 延迟加载、权限控制、远程访问 | 动态添加功能、组合特性 |
| 🎭 比喻 | 明星经纪人 | 煎饼加料 |
🎓 总结
代理模式是结构型模式中的实用利器,它完美诠释了:
🎯 “通过间接层来控制访问” 的设计哲学
通过引入代理对象,我们可以:
- ⏱️ 延迟加载 —— 省内存
- 🔒 权限控制 —— 保安全
- 📝 日志记录 —— 可追溯
- 💾 结果缓存 —— 提效率
📝 记忆口诀
“代理替身,控制访问;延迟加载,安全高效” 🎭
📝 本文通过 🎭 生活比喻、📊 UML类图、💻 完整代码案例,帮助你深入理解代理模式。希望对你有所帮助!
- 本文作者: 迪丽惹Bug
- 本文链接: https://lyroom.github.io/2026/04/27/代理模式详解/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!