一、什么是代理模式
代理模式是一种结构型设计模式,它为另一个对象提供一个替身或占位符以控制对这个对象的访问。代理模式在不修改原始对象的基础上,通过引入代理对象来扩展或控制对原始对象的访问。
二、核心角色
持有
<<interface>>
Subject
+request()
RealSubject
+request()
Proxy
-realSubject: RealSubject
+request()
+checkAccess()
+logAccess()
角色说明:
- Subject(抽象主题):定义真实对象和代理的共同接口
- RealSubject(真实主题):真正的业务逻辑对象
- Proxy(代理):持有对真实对象的引用,控制访问
三、代理模式分类与应用场景
1. 虚拟代理
场景 :延迟加载大对象,如高清图片、大文件等
示例:图片浏览器中的缩略图代理
2. 保护代理
场景 :控制访问权限,不同用户不同权限
示例:文档系统,管理员可读写,普通用户只读
3. 远程代理
场景 :隐藏对象在不同地址空间的事实
示例:RPC调用、Web服务代理
4. 智能引用代理
场景 :在访问对象时添加额外操作
示例:引用计数、日志记录、性能监控
四、C++代码示例
示例1:虚拟代理(图片加载)
cpp
#include <iostream>
#include <string>
#include <memory>
// 抽象主题
class Image {
public:
virtual ~Image() = default;
virtual void display() = 0;
};
// 真实主题:大尺寸图片
class HighResolutionImage : public Image {
private:
std::string filename;
void loadImageFromDisk() {
std::cout << "正在从磁盘加载高清图片: " << filename << " (耗时3秒)...\n";
// 模拟耗时加载
std::this_thread::sleep_for(std::chrono::seconds(1));
}
public:
HighResolutionImage(const std::string& name) : filename(name) {
loadImageFromDisk();
}
void display() override {
std::cout << "显示高清图片: " << filename << "\n";
}
};
// 代理:图片代理
class ImageProxy : public Image {
private:
std::string filename;
std::unique_ptr<HighResolutionImage> realImage;
public:
ImageProxy(const std::string& name) : filename(name), realImage(nullptr) {}
void display() override {
// 延迟加载:只有在真正需要时才创建真实对象
if (!realImage) {
realImage = std::make_unique<HighResolutionImage>(filename);
}
realImage->display();
}
};
// 使用示例
int main() {
// 创建代理对象(不会立即加载图片)
std::unique_ptr<Image> image = std::make_unique<ImageProxy>("photo.jpg");
std::cout << "图片对象已创建,但尚未加载到内存\n";
std::cout << "用户滚动到图片可见区域...\n";
// 真正需要显示时才加载
image->display();
return 0;
}
示例2:保护代理(权限控制)
cpp
#include <iostream>
#include <string>
#include <memory>
// 抽象主题
class Document {
public:
virtual ~Document() = default;
virtual void read() = 0;
virtual void write(const std::string& content) = 0;
};
// 真实主题
class RealDocument : public Document {
private:
std::string content;
std::string title;
public:
RealDocument(const std::string& t) : title(t), content("原始内容") {}
void read() override {
std::cout << "阅读文档《" << title << "》\n内容: " << content << "\n";
}
void write(const std::string& newContent) override {
content = newContent;
std::cout << "文档已更新\n";
}
};
// 代理:带权限控制的文档代理
class DocumentProxy : public Document {
private:
std::unique_ptr<RealDocument> realDoc;
std::string userRole;
bool hasReadPermission() {
return true; // 所有人可读
}
bool hasWritePermission() {
return userRole == "admin" || userRole == "editor";
}
public:
DocumentProxy(const std::string& title, const std::string& role)
: realDoc(std::make_unique<RealDocument>(title)), userRole(role) {}
void read() override {
if (hasReadPermission()) {
realDoc->read();
} else {
std::cout << "权限不足:无法读取文档\n";
}
}
void write(const std::string& content) override {
if (hasWritePermission()) {
realDoc->write(content);
} else {
std::cout << "权限不足:需要管理员或编辑权限才能写入\n";
}
}
};
// 使用示例
int main() {
std::cout << "=== 普通用户访问 ===\n";
auto userDoc = std::make_unique<DocumentProxy>("设计模式", "user");
userDoc->read(); // 成功
userDoc->write("新内容"); // 失败
std::cout << "\n=== 管理员访问 ===\n";
auto adminDoc = std::make_unique<DocumentProxy>("设计模式", "admin");
adminDoc->read(); // 成功
adminDoc->write("专业内容"); // 成功
return 0;
}
示例3:智能引用代理(日志记录)
cpp
#include <iostream>
#include <chrono>
#include <memory>
// 抽象主题
class Calculator {
public:
virtual ~Calculator() = default;
virtual int add(int a, int b) = 0;
virtual int multiply(int a, int b) = 0;
};
// 真实主题
class RealCalculator : public Calculator {
public:
int add(int a, int b) override {
return a + b;
}
int multiply(int a, int b) override {
return a * b;
}
};
// 代理:带日志和性能监控的计算器
class CalculatorProxy : public Calculator {
private:
std::unique_ptr<RealCalculator> realCalc;
void logOperation(const std::string& op, int a, int b, int result, double duration) {
std::cout << "[LOG] " << op << "(" << a << ", " << b
<< ") = " << result << " | 耗时: " << duration << "ms\n";
}
public:
CalculatorProxy() : realCalc(std::make_unique<RealCalculator>()) {}
int add(int a, int b) override {
auto start = std::chrono::high_resolution_clock::now();
// 前置处理
std::cout << "准备执行加法运算...\n";
// 调用真实对象
int result = realCalc->add(a, b);
// 后置处理
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration<double, std::milli>(end - start).count();
logOperation("add", a, b, result, duration);
return result;
}
int multiply(int a, int b) override {
auto start = std::chrono::high_resolution_clock::now();
std::cout << "准备执行乘法运算...\n";
int result = realCalc->multiply(a, b);
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration<double, std::milli>(end - start).count();
logOperation("multiply", a, b, result, duration);
return result;
}
};
// 使用示例
int main() {
auto calc = std::make_unique<CalculatorProxy>();
int sum = calc->add(10, 20);
std::cout << "结果: " << sum << "\n\n";
int product = calc->multiply(5, 6);
std::cout << "结果: " << product << "\n";
return 0;
}
五、代理模式优缺点
缺点
增加系统复杂度
处理速度变慢
多重代理复杂
优点
职责清晰
高扩展性
智能化
延迟加载
优点:
- 职责清晰:真实对象专注业务,代理负责辅助功能
- 高扩展性:可以在不修改真实对象的情况下增加功能
- 智能化:可以实现延迟加载、访问控制等智能行为
- 解耦合:客户端与真实对象解耦
缺点:
- 增加了系统复杂性
- 多一层代理可能影响处理速度
- 多重代理可能导致系统复杂难以维护
六、代理模式 vs 装饰器模式
| 对比项 | 代理模式 | 装饰器模式 |
|---|---|---|
| 目的 | 控制访问 | 增强功能 |
| 关系 | 编译时确定 | 运行时动态组合 |
| 创建 | 代理知道真实对象 | 客户端控制装饰顺序 |
| 重点 | 控制访问权限 | 添加新行为 |
七、实际应用场景
- 智能指针 :
std::shared_ptr是典型的智能引用代理 - 延迟加载框架:ORM框架中的懒加载
- AOP编程:Spring框架的AOP代理
- 网络代理:HTTP代理、反向代理
- 缓存代理:缓存计算结果
代理模式是一个非常实用的设计模式,在框架设计、权限控制、性能优化等场景中都有广泛应用。