一、定义与概念
- 定义
C++ 代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。在代理模式中,存在一个真实对象(被代理对象)和一个代理对象,客户端通过代理对象来间接访问真实对象,代理对象可以在访问真实对象前后进行一些额外的操作,比如权限检查、延迟加载、缓存等。 - 核心思想
通过引入代理对象来代替真实对象处理客户端的请求,代理对象和真实对象实现相同的接口,这样对于客户端来说,它不需要知道是在与真实对象还是代理对象打交道,而代理对象可以在必要时对请求进行预处理或后处理。
二、结构和组成部分
抽象主题(Subject)接口
- 定义:
这是真实对象和代理对象共同实现的接口,它定义了客户端可以调用的方法,确保了客户端对真实对象和代理对象的使用具有一致性。 - 代码示例(简单的文件读取接口)
cpp
class FileReaderSubject {
public:
virtual std::string readFile(const std::string& fileName) = 0;
virtual ~FileReaderSubject() {}
};
真实主题(Real Subject)
- 定义:
真实主题是被代理的对象,它实现了抽象主题接口,提供了真实的功能实现。 - 代码示例(实际的文件读取类)
cpp
class RealFileReader : public FileReaderSubject {
public:
std::string readFile(const std::string& fileName) override {
std::ifstream file(fileName);
if (file.is_open()) {
std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
file.close();
return content;
}
return "File not found or cannot be opened.";
}
};
代理(Proxy)
- 定义:
代理类也实现了抽象主题接口,它内部包含一个指向真实主题的指针。代理类在实现接口方法时,通常会先进行一些额外的操作,然后再调用真实主题的相应方法,最后可能还会进行一些后续操作。 - 代码示例(文件读取代理类,带缓存功能)
cpp
class FileReaderProxy : public FileReaderSubject {
private:
RealFileReader* realReader;
std::unordered_map<std::string, std::string> fileCache;
public:
FileReaderProxy() : realReader(nullptr) {}
std::string readFile(const std::string& fileName) override {
if (fileCache.find(fileName)!= fileCache.end()) {
return fileCache[fileName];
}
if (!realReader) {
realReader = new RealFileReader();
}
std::string content = realReader->readFile(fileName);
fileCache[fileName] = content;
return content;
}
~FileReaderProxy() {
delete realReader;
}
};
三、应用场景
远程代理
- 解释:
当客户端需要访问位于远程服务器上的对象时,可以使用远程代理。远程代理在本地代表远程对象,它负责与远程服务器进行通信,将客户端的请求发送到远程服务器,并将远程服务器的响应返回给客户端。这样,客户端就可以像访问本地对象一样访问远程对象,隐藏了远程调用的复杂性。 - 示例:
在分布式系统中,一个客户端想要访问远程数据库中的数据。通过创建远程数据库对象的代理,代理负责将客户端的数据查询请求发送到远程数据库服务器,接收服务器返回的数据,并将其传递给客户端。
虚拟代理
- 解释:
虚拟代理用于在需要时才创建和初始化被代理的对象,通常用于处理资源密集型对象。当客户端请求访问一个对象时,如果该对象尚未创建或初始化,虚拟代理会先进行一些轻量级的操作,如显示一个占位符,然后在合适的时候再创建和初始化真实对象,并将客户端的请求转发给真实对象。 - 示例:
在一个图像浏览系统中,当用户浏览图像列表时,图像可能是高分辨率的资源密集型对象。此时可以使用虚拟代理,在图像未完全加载之前,代理显示一个低分辨率的占位图像,当用户真正点击查看某张图像时,虚拟代理再去加载和显示高分辨率的真实图像。
保护代理
- 解释:
保护代理用于控制对真实对象的访问权限。它可以在客户端访问真实对象之前进行权限检查,如果客户端具有访问权限,则允许访问,否则拒绝访问。这样可以保护真实对象不被未授权的客户端访问。 - 示例:
在企业内部的文件管理系统中,有一些机密文件只有特定的用户角色(如管理员或具有特定权限的员工)可以访问。通过使用保护代理,在用户请求访问文件时,代理先检查用户的权限,如果权限符合要求,则调用真实的文件读取操作,否则返回访问被拒绝的提示。
四、优缺点
优点
- 访问控制:
可以有效地控制对真实对象的访问,通过代理对象进行权限检查等操作,保护真实对象的安全性。 - 增强功能:
代理对象可以在不改变真实对象的基础上,为真实对象添加额外的功能,如缓存、延迟加载、性能优化等,提高了系统的灵活性和可扩展性。 - 解耦:
代理模式将客户端与真实对象解耦,客户端不需要了解真实对象的具体实现细节,即使真实对象的实现发生变化,只要代理对象和真实对象之间的接口不变,客户端代码就不需要修改。
缺点
- 增加复杂性:
引入代理对象增加了系统的复杂性,特别是当存在多个代理类且它们的逻辑较为复杂时,代码的理解和维护成本会增加。 - 性能开销:
在某些情况下,代理对象的额外操作(如权限检查、缓存管理等)可能会带来一定的性能开销,尤其是在对性能要求极高的场景中,需要谨慎考虑代理模式的使用。
代理模式在很多场景下能够为系统带来诸多好处,但需要综合考虑其优缺点,根据实际需求合理使用。