代理模式(Proxy Pattern)是一种结构型设计模式,它为另一个对象提供一个代理或占位符,以控制对原对象的访问。这种模式通过引入代理对象,在不改变原对象的前提下,实现对原对象的访问控制、增强或扩展。
代理模式的核心角色
- Subject(抽象主题):定义原对象和代理对象的共同接口,使代理可被当作原对象使用
- RealSubject(真实主题):被代理的实际对象,提供核心业务逻辑
- Proxy(代理):实现抽象主题,持有真实主题的引用,控制对真实主题的访问
常见代理类型及C++实现示例
1. 远程代理(Remote Proxy)
为远程对象提供本地代理,隐藏网络通信细节。
cpp
#include <iostream>
#include <string>
#include <stdexcept>
// 抽象主题:文件接口
class File {
public:
virtual ~File() = default;
virtual void read() = 0;
virtual void write(const std::string& content) = 0;
};
// 真实主题:远程文件(位于服务器)
class RemoteFile : public File {
private:
std::string fileName;
std::string serverAddress;
public:
RemoteFile(std::string name, std::string address)
: fileName(std::move(name)), serverAddress(std::move(address)) {
// 模拟远程连接
std::cout << "连接到远程服务器: " << serverAddress << std::endl;
}
void read() override {
// 模拟远程读取
std::cout << "从 " << serverAddress << " 读取文件: " << fileName << std::endl;
}
void write(const std::string& content) override {
// 模拟远程写入
std::cout << "向 " << serverAddress << " 的 " << fileName
<< " 写入内容: " << content << std::endl;
}
};
// 代理:远程文件代理(本地代理)
class RemoteFileProxy : public File {
private:
std::string fileName;
std::string serverAddress;
RemoteFile* remoteFile = nullptr; // 延迟初始化远程对象
// 确保远程对象已初始化
void ensureConnected() {
if (!remoteFile) {
// 可以在这里添加权限校验、连接池等逻辑
try {
remoteFile = new RemoteFile(fileName, serverAddress);
} catch (const std::exception& e) {
std::cerr << "连接远程文件失败: " << e.what() << std::endl;
throw;
}
}
}
public:
RemoteFileProxy(std::string name, std::string address)
: fileName(std::move(name)), serverAddress(std::move(address)) {}
~RemoteFileProxy() override {
delete remoteFile; // 释放远程对象
}
// 代理读取操作
void read() override {
std::cout << "RemoteFileProxy: 准备读取远程文件" << std::endl;
ensureConnected();
remoteFile->read();
std::cout << "RemoteFileProxy: 读取完成" << std::endl;
}
// 代理写入操作
void write(const std::string& content) override {
std::cout << "RemoteFileProxy: 准备写入远程文件" << std::endl;
ensureConnected();
remoteFile->write(content);
std::cout << "RemoteFileProxy: 写入完成" << std::endl;
}
};
// 客户端代码
void clientCode(File* file) {
file->read();
file->write("代理模式测试内容");
}
int main() {
try {
// 使用代理访问远程文件,客户端无需知道远程细节
File* fileProxy = new RemoteFileProxy("data.txt", "http://remote-server.com");
clientCode(fileProxy);
delete fileProxy;
} catch (const std::exception& e) {
std::cerr << "客户端错误: " << e.what() << std::endl;
}
return 0;
}
2. 虚拟代理(Virtual Proxy)
延迟创建开销大的对象,仅在需要时才初始化。
cpp
#include <iostream>
#include <string>
#include <memory>
// 抽象主题:图片接口
class Image {
public:
virtual ~Image() = default;
virtual void display() = 0;
};
// 真实主题:大图片(加载开销大)
class BigImage : public Image {
private:
std::string filePath;
// 模拟加载大图片的耗时操作
void loadFromDisk() {
std::cout << "正在从磁盘加载大图片: " << filePath << " (耗时操作)" << std::endl;
}
public:
BigImage(std::string path) : filePath(std::move(path)) {
loadFromDisk(); // 构造时加载
}
void display() override {
std::cout << "显示图片: " << filePath << std::endl;
}
};
// 虚拟代理:图片代理(延迟加载)
class ImageProxy : public Image {
private:
std::string filePath;
std::unique_ptr<BigImage> bigImage; // 延迟初始化
public:
ImageProxy(std::string path) : filePath(std::move(path)) {
std::cout << "创建图片代理: " << filePath << " (未实际加载)" << std::endl;
}
// 仅在需要显示时才加载真实图片
void display() override {
std::cout << "ImageProxy: 准备显示图片" << std::endl;
if (!bigImage) {
bigImage = std::make_unique<BigImage>(filePath); // 延迟加载
}
bigImage->display();
}
};
// 客户端代码
void showImage(Image* image) {
std::cout << "请求显示图片..." << std::endl;
image->display();
std::cout << "图片显示结束" << std::endl;
}
int main() {
// 创建代理时不会加载图片
Image* image = new ImageProxy("large_image.jpg");
// 执行其他操作,图片未被加载
std::cout << "\n执行一些其他操作..." << std::endl;
// 只有当需要显示时,才会真正加载图片
std::cout << "\n第一次显示图片:" << std::endl;
showImage(image);
// 第二次显示时,已加载过,直接显示
std::cout << "\n第二次显示图片:" << std::endl;
showImage(image);
delete image;
return 0;
}
3. 保护代理(Protection Proxy)
控制对敏感对象的访问,基于权限过滤请求。
cpp
#include <iostream>
#include <string>
#include <stdexcept>
// 用户类:存储用户信息和权限
class User {
private:
std::string name;
bool isAdmin;
public:
User(std::string n, bool admin) : name(std::move(n)), isAdmin(admin) {}
const std::string& getName() const { return name; }
bool hasAdminRights() const { return isAdmin; }
};
// 抽象主题:敏感数据接口
class SensitiveData {
public:
virtual ~SensitiveData() = default;
virtual std::string read() const = 0;
virtual void write(const std::string& data) = 0;
};
// 真实主题:实际的敏感数据
class RealSensitiveData : public SensitiveData {
private:
std::string data;
public:
RealSensitiveData(std::string d) : data(std::move(d)) {}
std::string read() const override {
return data;
}
void write(const std::string& newData) override {
data = newData;
}
};
// 保护代理:控制敏感数据的访问
class ProtectionProxy : public SensitiveData {
private:
RealSensitiveData* realData;
User user; // 当前用户
public:
ProtectionProxy(RealSensitiveData* data, User u)
: realData(data), user(std::move(u)) {}
~ProtectionProxy() override {
delete realData;
}
// 读取权限控制:普通用户可读,管理员可写
std::string read() const override {
std::cout << "ProtectionProxy: " << user.getName() << " 请求读取数据" << std::endl;
// 任何人都可以读取
return realData->read();
}
// 写入权限控制:仅管理员可写
void write(const std::string& newData) override {
std::cout << "ProtectionProxy: " << user.getName() << " 请求写入数据" << std::endl;
if (user.hasAdminRights()) {
realData->write(newData);
std::cout << "ProtectionProxy: 写入成功(管理员权限)" << std::endl;
} else {
throw std::runtime_error("权限不足:只有管理员可以修改敏感数据");
}
}
};
// 客户端代码
void accessData(SensitiveData* data) {
try {
std::cout << "读取数据: " << data->read() << std::endl;
data->write("更新的数据内容");
std::cout << "更新后的数据: " << data->read() << std::endl;
} catch (const std::exception& e) {
std::cerr << "操作失败: " << e.what() << std::endl;
}
}
int main() {
// 创建真实敏感数据
RealSensitiveData* realData = new RealSensitiveData("初始敏感数据");
std::cout << "=== 管理员访问 ===" << std::endl;
User admin("管理员", true);
SensitiveData* adminProxy = new ProtectionProxy(realData, admin);
accessData(adminProxy);
std::cout << "\n=== 普通用户访问 ===" << std::endl;
// 注意:这里需要创建新的真实数据,因为上一个代理已经接管了realData的所有权
RealSensitiveData* realData2 = new RealSensitiveData("初始敏感数据");
User guest("普通用户", false);
SensitiveData* guestProxy = new ProtectionProxy(realData2, guest);
accessData(guestProxy);
delete adminProxy;
delete guestProxy;
return 0;
}
代理模式的核心特点
- 控制访问:代理可在调用真实对象前/后添加逻辑(如权限校验、日志记录)
- 延迟初始化:虚拟代理可推迟昂贵对象的创建,直到真正需要时
- 隐藏细节:远程代理屏蔽了对象位于远程的事实,使客户端透明访问
- 增强功能:在不修改原对象的情况下,通过代理添加额外功能(如缓存、计数)
代理模式与装饰器模式的区别
特性 | 代理模式 | 装饰器模式 |
---|---|---|
核心目的 | 控制对对象的访问 | 为对象动态添加功能 |
关系 | 代理通常知道真实对象的类型 | 装饰器可嵌套,通常不知道具体装饰的对象 |
使用场景 | 权限控制、远程访问、延迟加载 | 功能扩展、组合多个功能 |
透明度 | 客户端可能知道代理的存在 | 客户端通常不知道装饰器的存在 |
适用场景
- 当需要控制对敏感对象的访问时(保护代理)
- 当对象创建成本高,希望延迟初始化时(虚拟代理)
- 当需要访问远程对象,隐藏网络细节时(远程代理)
- 当需要为对象的访问添加日志、缓存等功能时(智能引用代理)
代理模式在框架中应用广泛,如RPC框架中的服务代理、ORM框架中的数据库代理、AOP中的方法拦截等。