关于 gof-proxy
讲解代理模式的设计模式技能,介绍如何在保持相同接口的同时控制对对象的访问。内容区分虚拟、远程和保护代理,并说明其与装饰器、适配器的区别。
主要功能
- 01虚拟、远程与保护代理
- 02延迟创建昂贵对象
- 03转发对远程对象的调用
- 04调用前进行权限检查
- 05代理模式的判断指引与陷阱
适用场景
- 01延迟创建大型资源
- 02在本地表示远程对象
- 03在方法调用前检查权限
- 04使用替身简化测试
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ yonatankarp/ software-design-skills --skill gof-proxy安装后在 Claude Code 中输入/gof-proxy确认已加载。
详细介绍
一句话概述
为另一个对象提供替身或占位对象,以控制对它的访问——同时让客户端不知道自己并非直接与真实对象交互。
何时使用此技能
- 创建真实对象的成本很高,且应推迟到实际使用时再创建(虚拟代理——加载图像、延迟初始化重量级资源)。
- 真实对象位于另一个进程中或另一台机器上,而客户端接口应看起来像本地接口(远程代理——RMI、gRPC 存根、RPC 客户端)。
- 应根据权限或速率限制检查对真实对象的访问(保护代理)。
- 真实对象难以在测试中模拟,而使用替身可以让测试更轻松。
何时不应使用此技能
- 你想为包装对象添加行为——应使用
gof-decorator,而不是 Proxy。两者的结构形式相同,但意图不同。 - 你想更改接口——应使用
gof-adapter。 - 延迟初始化一个简单字段——大多数语言都有可以处理此需求的延迟委托(Kotlin 中的
by lazy、C# 中的lazy),无需代理类。
核心内容
Proxy 实现与真实对象相同的接口。客户端持有对代理的引用并调用其方法;代理决定何时以及是否将调用转发给真实主题对象。
三种常见类型:
虚拟代理。 首次使用时创建真实对象。对于“低成本”操作(例如,getId() 返回存储的 ID),客户端可以立即得到响应;只有实际调用 getData() 时,才会创建开销较大的对象。
远程代理。 远程对象的本地替身。代理会序列化参数,通过网络发送它们,反序列化响应并将其返回。客户端编写代码时,就像远程对象位于本地一样。(注意:假装远程调用与本地调用无异也是一种已知反模式,因为调用方可能忘记网络故障、延迟和部分结果——参见“分布式计算的八个谬误”。)
保护代理。 用权限检查包装真实对象。代理的每个方法都会先验证调用方的权限,然后再转发调用(或拒绝调用)。
判断启发
- 虚拟代理:只有在创建成本很高且大多数调用方实际上不会需要真实对象时才合理。否则直接创建即可。
- 远程代理:到了 2026 年,几乎不会再手动编写——gRPC、OpenAPI、GraphQL 都会生成代理代码。理解这一模式很重要;通常没有必要手动实现。
- 保护代理:需谨慎——如果可以绕过安全包装器,它就算不上真正的保护。应在边界处(控制器/API 层)执行权限检查,而不是通过进程内代理。
反模式
- 添加行为的代理——这是披着 Proxy 外衣的 Decorator。
- 会随时间不断增加行为的“智能代理”——到某个时候它就不再是代理了;应进行重构。
- 过度隐藏远程调用语义,让调用方不知道调用发生在远程——这会导致 N+1 网络调用和令人措手不及的生产事故。
另请参阅
gof-decorator——结构形式相同,意图不同(Decorator 增加职责;Proxy 控制访问)。gof-adapter——用于更改接口,而不只是控制访问。gof-kotlin-idioms——字段的虚拟代理可由by lazy实现;远程代理可由框架生成的存根(gRPC、Retrofit)实现。自行编写的情况很少见,通常也不妥。
参考资料
- Head First Design Patterns(第 2 版),第 11 章(“控制对象访问:Proxy 模式”)——经典章节,介绍了虚拟代理、远程代理(通过 Java RMI)和保护代理。
评论
支持 Markdown