1. 代理模式概念
代理模式是一种结构型设计模式,为其他对象提供一种代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用,可以用于:
-
控制对目标对象的访问
-
增强目标对象的功能
-
延迟目标对象的创建和初始化
2. 代理模式的主要类型
| 类型 | 描述 | 应用场景 |
|---|---|---|
| 虚拟代理 | 延迟创建开销大的对象 | 图片懒加载、大文件延迟加载 |
| 保护代理 | 控制访问权限 | 权限验证、访问控制 |
| 远程代理 | 为远程对象提供本地代表 | RPC调用、网络服务 |
| 智能引用 | 在访问对象时添加额外操作 | 引用计数、日志记录 |
| 缓存代理 | 为开销大的运算结果提供缓存 | 计算缓存、数据库查询缓存 |
3. UML 结构
┌─────────────┐ ┌─────────────┐
│ Subject │ │ Real │
│ +request() │<─────>│ Subject │
└─────────────┘ │ +request() │
△ └─────────────┘
│ △
│ │
┌─────────────┐ ┌─────────────┐
│ Proxy │──────>│ Client │
│ +request() │ │ │
└─────────────┘ └─────────────┘
4. C++ 代码示例
示例1:虚拟代理(图片加载)
cpp
#include <iostream>
#include <string>
#include <memory>
#include <thread>
#include <chrono>
// 1. 抽象主题接口
class Image {
public:
virtual ~Image() = default;
virtual void display() = 0;
};
// 2. 真实主题
class RealImage : public Image {
private:
std::string filename;
void loadFromDisk() {
std::cout << "正在从磁盘加载图片: " << filename << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时操作
std::cout << "图片加载完成!" << std::endl;
}
public:
RealImage(const std::string& filename) : filename(filename) {
loadFromDisk();
}
void display() override {
std::cout << "显示图片: " << filename << std::endl;
}
};
// 3. 代理类
class ProxyImage : public Image {
private:
std::string filename;
std::unique_ptr<RealImage> realImage; // 延迟加载
public:
ProxyImage(const std::string& filename)
: filename(filename), realImage(nullptr) {}
void display() override {
// 延迟初始化:只在真正需要时才创建真实对象
if (realImage == nullptr) {
realImage = std::make_unique<RealImage>(filename);
}
realImage->display();
}
};
// 客户端代码
int main() {
std::cout << "=== 虚拟代理示例(图片懒加载)===" << std::endl;
// 创建代理,此时不会立即加载图片
ProxyImage proxy("high_resolution_photo.jpg");
std::cout << "\n1. 第一次调用display():" << std::endl;
proxy.display(); // 此时才会真正加载图片
std::cout << "\n2. 第二次调用display():" << std::endl;
proxy.display(); // 直接显示,不再重新加载
return 0;
}
示例2:保护代理(访问控制)
cpp
#include <iostream>
#include <string>
#include <memory>
// 1. 抽象主题接口
class Database {
public:
virtual ~Database() = default;
virtual void query(const std::string& sql) = 0;
};
// 2. 真实主题
class RealDatabase : public Database {
public:
void query(const std::string& sql) override {
std::cout << "执行SQL查询: " << sql << std::endl;
// 实际数据库操作...
}
};
// 3. 代理类(保护代理)
class DatabaseProxy : public Database {
private:
std::unique_ptr<RealDatabase> realDb;
std::string userRole;
bool hasPermission(const std::string& sql) {
// 检查SQL语句类型
std::string upperSql = sql;
for (char& c : upperSql) c = toupper(c);
if (upperSql.find("DELETE") != std::string::npos ||
upperSql.find("DROP") != std::string::npos ||
upperSql.find("UPDATE") != std::string::npos) {
return userRole == "admin";
}
return true; // SELECT 语句允许所有用户
}
public:
DatabaseProxy(const std::string& role)
: realDb(std::make_unique<RealDatabase>()), userRole(role) {}
void query(const std::string& sql) override {
if (hasPermission(sql)) {
std::cout << "[权限验证通过] ";
realDb->query(sql);
} else {
std::cout << "[权限拒绝] 用户'" << userRole
<< "'无权执行此操作: " << sql << std::endl;
}
}
void setRole(const std::string& role) {
userRole = role;
}
};
// 客户端代码
int main() {
std::cout << "=== 保护代理示例(数据库权限控制)===" << std::endl;
// 普通用户
DatabaseProxy userProxy("user");
std::cout << "\n1. 普通用户操作:" << std::endl;
userProxy.query("SELECT * FROM users");
userProxy.query("DELETE FROM users WHERE id=1");
// 管理员
DatabaseProxy adminProxy("admin");
std::cout << "\n2. 管理员操作:" << std::endl;
adminProxy.query("DELETE FROM users WHERE id=1");
// 动态切换角色
std::cout << "\n3. 动态切换角色:" << std::endl;
DatabaseProxy dynamicProxy("user");
dynamicProxy.query("DROP TABLE users");
dynamicProxy.setRole("admin");
dynamicProxy.query("DROP TABLE users");
return 0;
}
示例3:智能引用代理(带缓存)
cpp
#include <iostream>
#include <string>
#include <unordered_map>
#include <memory>
#include <cmath>
#include <chrono>
// 1. 抽象主题接口
class MathService {
public:
virtual ~MathService() = default;
virtual double compute(int value) = 0;
};
// 2. 真实主题(计算密集型操作)
class RealMathService : public MathService {
public:
double compute(int value) override {
// 模拟耗时计算
std::cout << "正在计算 " << value << " 的平方根..." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(500));
return sqrt(static_cast<double>(value));
}
};
// 3. 代理类(带缓存)
class CachedMathService : public MathService {
private:
std::unique_ptr<RealMathService> realService;
std::unordered_map<int, double> cache;
int cacheHits = 0;
int totalRequests = 0;
public:
CachedMathService()
: realService(std::make_unique<RealMathService>()) {}
double compute(int value) override {
totalRequests++;
// 检查缓存
auto it = cache.find(value);
if (it != cache.end()) {
cacheHits++;
std::cout << "[缓存命中] ";
return it->second;
}
// 计算并缓存结果
std::cout << "[缓存未命中] ";
double result = realService->compute(value);
cache[value] = result;
return result;
}
void showStats() const {
std::cout << "\n=== 缓存统计 ===" << std::endl;
std::cout << "总请求数: " << totalRequests << std::endl;
std::cout << "缓存命中数: " << cacheHits << std::endl;
std::cout << "缓存命中率: "
<< (totalRequests > 0 ? (100.0 * cacheHits / totalRequests) : 0)
<< "%" << std::endl;
std::cout << "缓存大小: " << cache.size() << std::endl;
}
void clearCache() {
cache.clear();
cacheHits = 0;
totalRequests = 0;
std::cout << "缓存已清空" << std::endl;
}
};
// 客户端代码
int main() {
std::cout << "=== 缓存代理示例(计算缓存)===" << std::endl;
CachedMathService mathService;
// 第一次计算
std::cout << "\n1. 第一次计算相同数值:" << std::endl;
std::cout << "结果: " << mathService.compute(16) << std::endl;
std::cout << "结果: " << mathService.compute(16) << std::endl;
std::cout << "结果: " << mathService.compute(16) << std::endl;
// 计算不同数值
std::cout << "\n2. 计算不同数值:" << std::endl;
for (int i = 1; i <= 5; i++) {
std::cout << "sqrt(" << i << ") = " << mathService.compute(i) << std::endl;
}
// 再次计算
std::cout << "\n3. 再次计算相同数值:" << std::endl;
for (int i = 1; i <= 5; i++) {
std::cout << "sqrt(" << i << ") = " << mathService.compute(i) << std::endl;
}
// 显示统计信息
mathService.showStats();
return 0;
}
5. 代理模式的优缺点
优点:
-
✅ 职责清晰:真实主题只需关注核心业务逻辑
-
✅ 高扩展性:可以在不修改真实主题的情况下增加功能
-
✅ 安全控制:保护代理可以控制对真实主题的访问权限
-
✅ 性能优化:虚拟代理可以延迟加载,缓存代理可以减少重复计算