代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理模式可以在不修改原始对象的情况下,添加额外的功能或处理逻辑。
使用场景
-
控制对复杂或敏感对象的访问
当需要控制对某些复杂或敏感对象的访问时,可以使用代理模式来封装这些对象的访问逻辑。示例:虚拟代理(Virtual Proxy),用于控制对大型资源(如大文件或大图片)的访问,只有在真正需要时才加载这些资源。
-
延迟加载(Lazy Loading)
当创建对象的开销很大时,可以使用代理模式延迟对象的创建,只有在第一次访问对象时才创建它。示例:在一个图形编辑器中,只有在用户真正需要查看大图片时,才会加载和显示这些图片。
-
提供远程对象的代理(Remote Proxy)
当对象在不同的地址空间或远程服务器上时,可以使用代理模式来封装网络通信细节,使得客户端像访问本地对象一样访问远程对象。示例:RPC(远程过程调用)框架,客户端通过代理对象调用远程服务器上的方法。
-
为对象添加额外的功能
代理模式可以在不修改原始对象的情况下,为对象添加额外的功能或处理逻辑。示例:保护代理(Protection Proxy),用于控制对敏感对象的方法调用,确保调用者具有适当的权限。
-
分离职责: 代理模式可以将实际工作与控制逻辑分开,使代码更清晰、更易于维护。
示例:将访问控制、性能监测等职责从业务逻辑中分离出来。
UML类图
+-------------------+ +-------------------+
| Subject |<>---->| Proxy |
+-------------------+ +-------------------+
| +request() | | -realSubject: RealSubject |
+-------------------+ | +request() |
+-------------------+
/|\
|
|
+-------------------+
| RealSubject |
+-------------------+
| +request() |
+-------------------+
示例代码
假设我们有一个图片查看器,当用户想查看大图片时,只有在真正需要时才加载图片。我们用代理模式来实现这个场景。
#include <iostream>
#include <memory>
// 抽象主题类
class Image {
public:
virtual void display() = 0;
virtual ~Image() = default;
};
// 真实主题类
class RealImage : public Image {
public:
RealImage(const std::string& filename) : filename_(filename) {
loadFromDisk();
}
void display() override {
std::cout << "Displaying " << filename_ << std::endl;
}
private:
void loadFromDisk() {
std::cout << "Loading " << filename_ << " from disk..." << std::endl;
}
std::string filename_;
};
// 代理类
class ProxyImage : public Image {
public:
ProxyImage(const std::string& filename) : filename_(filename) {}
void display() override {
if (!realImage_) {
realImage_ = std::make_unique<RealImage>(filename_);
}
realImage_->display();
}
private:
std::string filename_;
std::unique_ptr<RealImage> realImage_;
};
// 客户端代码
int main() {
std::unique_ptr<Image> image = std::make_unique<ProxyImage>("large_image.jpg");
// 图片第一次显示时,加载并显示图片
image->display();
// 图片第二次显示时,不需要重新加载,直接显示
image->display();
return 0;
}
代码解读
- 抽象主题类(Image):定义了
display
方法,所有具体主题和代理类都需要实现这个方法。 - 真实主题类(RealImage):实现了
Image
接口,包含加载和显示图片的逻辑。在构造函数中加载图片。 - 代理类(ProxyImage):也实现了
Image
接口,持有一个RealImage
对象的指针。在第一次调用display
方法时,创建并加载RealImage
对象,后续调用直接使用已经加载的对象。 - 客户端代码:通过代理类
ProxyImage
访问和显示图片。第一次调用display
时加载图片,后续调用直接显示。
使用场景总结
- 控制对复杂或敏感对象的访问:如虚拟代理控制对大型资源的访问。
- 延迟加载(Lazy Loading):如在图形编辑器中延迟加载大图片。
- 提供远程对象的代理(Remote Proxy):如RPC框架中的远程方法调用。
- 为对象添加额外的功能:如保护代理控制对敏感对象的方法调用。
缺点
-
增加系统复杂性: 代理模式引入了额外的代理类和代理对象,使系统结构变得更加复杂,增加了理解和维护的难度。
示例:对于简单的对象访问,引入代理模式可能显得过于复杂和冗余。
-
性能开销: 代理模式会增加一些额外的处理开销,如方法调用的间接性和对象的创建时间等,可能会影响系统性能。
示例:对于需要频繁访问的对象,代理模式可能带来性能上的额外开销。
-
实现成本: 代理模式的实现需要额外的代码和逻辑,如代理类的实现和方法的重定向等,增加了开发成本。
示例:实现远程代理时,需要处理网络通信、序列化和反序列化等复杂逻辑。
通过这些例子和解释,可以看到代理模式在控制对象访问、延迟加载、提供远程对象代理以及为对象添加功能方面的强大功能,同时也要注意它可能带来的复杂性和性能开销。