代理模式 (Proxy)
代理模式 是一种结构型设计模式,它为其他对象提供一个代理以控制对该对象的访问。代理模式常用于延迟加载、访问控制、智能引用等场景。
意图
- 提供对某对象的控制。
- 控制对目标对象的访问,通常用于在不改变目标对象的情况下,附加额外的功能。
- 常见于延迟加载、权限控制等场景。
使用场景
-
远程代理
- 当对象存在于不同的地址空间时,代理模式提供了对远程对象的访问。
-
虚拟代理
- 延迟对象的创建,只有在需要时才创建对象,避免不必要的资源消耗。
-
保护代理
- 控制对某个对象的访问权限。代理对象负责控制访问。
-
智能代理
- 对对象访问进行计数、缓存等操作。例如,记录访问次数、管理缓存等。
参与者角色
-
主题接口 (Subject)
- 定义了目标对象和代理类的公共接口。
-
真实主题 (RealSubject)
- 实现了
Subject
接口的目标对象,包含实际业务逻辑。
- 实现了
-
代理 (Proxy)
- 代理类,持有真实主题对象的引用,代理请求到真实主题对象,附加控制逻辑。
示例代码
以下代码展示了如何使用代理模式控制对一个图片加载的访问。
cpp
#include <iostream>
#include <string>
// 主题接口
class Image {
public:
virtual ~Image() = default;
virtual void display() = 0; // 移除 const
};
// 真实主题:加载并显示图片
class RealImage : public Image {
private:
std::string filename;
public:
RealImage(const std::string& file) : filename(file) {
loadImage();
}
void loadImage() const {
std::cout << "Loading image: " << filename << std::endl;
}
void display() override {
std::cout << "Displaying image: " << filename << std::endl;
}
};
// 代理类:代理真实图片加载和显示
class ProxyImage : public Image {
private:
RealImage* realImage;
std::string filename;
public:
ProxyImage(const std::string& file) : filename(file), realImage(nullptr) {}
void display() override {
if (!realImage) {
realImage = new RealImage(filename); // 延迟加载真实图像
}
realImage->display();
}
~ProxyImage() {
delete realImage;
}
};
// 客户端代码
int main() {
Image* image1 = new ProxyImage("image1.jpg");
Image* image2 = new ProxyImage("image2.jpg");
// 第一次访问时加载并显示图片
image1->display();
image2->display();
// 直接显示图片,不会重新加载
image1->display();
delete image1;
delete image2;
return 0;
}
代码解析
1. 主题接口
Image
类定义了目标对象和代理类的公共接口。- 所有的图片类都需要实现
display
方法。
cpp
class Image {
public:
virtual ~Image() = default;
virtual void display() = 0;
};
2. 真实主题
RealImage
类是实际的图片对象,提供了加载和显示图片的功能。loadImage
在构造函数中被调用,用于模拟加载图片。
cpp
class RealImage : public Image {
private:
std::string filename;
public:
RealImage(const std::string& file) : filename(file) {
loadImage();
}
void loadImage() const {
std::cout << "Loading image: " << filename << std::endl;
}
void display() override {
std::cout << "Displaying image: " << filename << std::endl;
}
};
3. 代理类
ProxyImage
类是一个代理类,它控制对RealImage
的访问。- 在
display
方法中,它会先检查realImage
是否已被创建。如果尚未创建,则延迟加载它。
cpp
class ProxyImage : public Image {
private:
RealImage* realImage;
std::string filename;
public:
ProxyImage(const std::string& file) : filename(file), realImage(nullptr) {}
void display() override {
if (!realImage) {
realImage = new RealImage(filename); // 延迟加载真实图像
}
realImage->display();
}
~ProxyImage() {
delete realImage;
}
};
4. 客户端
- 客户端使用代理类,而不是直接使用真实主题类。
ProxyImage
控制了对RealImage
的访问,并在必要时创建它。
cpp
int main() {
Image* image1 = new ProxyImage("image1.jpg");
Image* image2 = new ProxyImage("image2.jpg");
image1->display(); // 加载并显示
image2->display(); // 加载并显示
image1->display(); // 直接显示
delete image1;
delete image2;
}
优缺点
优点
-
延迟初始化:
- 代理模式可以在需要时才创建和初始化对象,避免不必要的资源消耗。
-
控制访问权限:
- 代理对象可以控制对真实对象的访问,如权限控制、延迟加载等。
-
增加功能而不改变目标对象:
- 通过代理,可以为目标对象添加附加功能,而无需修改目标对象。
缺点
-
增加系统复杂度:
- 代理模式增加了对象间的间接性,可能导致系统变得复杂。
-
性能开销:
- 使用代理时,可能会有额外的性能开销,特别是在频繁调用代理方法时。
适用场景
-
远程代理:
- 访问远程对象时,代理对象可以替代真实对象,并处理通信。
-
虚拟代理:
- 延迟对象的加载,当需要时才创建对象,避免不必要的资源消耗。
-
保护代理:
- 控制对真实对象的访问权限,确保只有符合条件的用户才能访问。
总结
代理模式通过提供一个替代的对象来控制对真实对象的访问。它广泛用于延迟加载、权限控制、远程调用等场景,能够有效提高系统的灵活性和可扩展性。