代理模式(Proxy):替身挡在前面,控制对真身的访问
更新时间:2026-08-25。本文按"先看不用模式的困境 → 再用模式重构 → 类图对比分析"的结构,论述代理模式。可运行代码:demos/design-patterns/proxy。
一、问题场景:访问真身太贵/太远/太危险
图片懒加载(真图加载慢)、远程服务调用(RPC 包装网络细节)、敏感资源(权限校验)、引用计数。客户端想要"像用真身一样"地用,但访问过程需要额外控制。
二、不使用模式:客户端直接持有并访问真身
cpp
// 客户端直接持有真图片,启动即全量加载
class HeavyImage { public: void draw() { load_full(); /* 慢 */ } };
// 客户端每次启动都要等完整加载;无权限也能直接调
HeavyImage img; img.draw(); // 无法懒加载、无法加权限| 缺陷 | 说明 |
|---|---|
| 无法懒加载 | 昂贵对象在客户端持有时就已构造 |
| 无法做访问控制 | 敏感操作无权限校验点 |
| 无法加缓存/日志 | 控制逻辑无处安放 |
| 客户端耦合真身 | 换实现/加控制要改所有调用点 |
不使用模式的类图

三、使用模式:代理实现同接口,内部控制访问
代理模式让代理与真身实现同一接口,客户端只依赖接口;代理内部持有真身引用,在调用前后插入控制逻辑(懒加载、权限、缓存、日志、远程转发),对客户端透明。
cpp
// image_proxy.h
class Image { // 抽象主题
public:
virtual void draw() = 0;
virtual ~Image() = default;
};
class RealImage : public Image { // 真身
public:
explicit RealImage(std::string path) : path_(std::move(path)) {}
void draw() override { loadFull(); doDraw(); } // 昂贵
private:
std::string path_;
void loadFull() { /* 从磁盘读整图,慢 */ }
void doDraw() {}
};
class ImageProxy : public Image { // 代理
public:
explicit ImageProxy(std::string path) : path_(std::move(path)) {}
void draw() override {
if (!real_) real_ = std::make_unique<RealImage>(path_); // 懒加载
real_->draw(); // 委托真身
}
private:
std::string path_;
std::unique_ptr<RealImage> real_; // 首次 draw 才创建
};
// 客户端:只依赖 Image 接口,无感知
void show(Image& img) { img.draw(); } // 传 RealImage 或 ImageProxy 均可使用模式的类图

四、类图对比与分析
| 维度 | 不使用(直接访问真身) | 使用代理 |
|---|---|---|
| 懒加载 | 无(持有即构造) | 首次访问才建真身 |
| 访问控制 | 无校验点 | 代理内校验 |
| 附加能力(缓存/日志) | 无处安放 | 代理内统一插入 |
| 客户端耦合 | 具体真身类 | 抽象接口 |
| 透明性 | — | 客户端无感知(LSP) |
分析结论:代理的本质是"拦截 + 委托"——在接口不改变的前提下,把访问过程的可控性交给代理层。它与装饰器结构几乎一样(都持真身引用、同接口),区别在意图:代理管"访问"(要不要给、何时建、加不加缓存),装饰器管"能力"(加功能、多层叠加)。
五、适用边界
| 适用 | 不适用 |
|---|---|
| 昂贵对象需懒加载/缓存 | 对象廉价、无控制需求(代理是冗余) |
| 需要权限校验/审计/日志 | 控制逻辑简单到可以直接写进真身 |
| 远程对象透明访问(RPC 桩) | 接口需要改变(该用适配器) |
代理变体:远程代理(RPC 桩)、虚拟代理(懒加载)、保护代理(权限)、缓存代理、智能引用(引用计数/释放)。C++ 中 std::shared_ptr 的引用计数管理就是"智能引用代理"的思想。
一句话总结
代理模式让代理与真身同接口、内部持有真身并在调用前后插入控制,客户端透明使用——懒加载、权限、缓存、远程转发都收进代理层;与装饰器结构相似但意图不同:代理控制"访问",装饰器增强"能力"。