什么是代理模式?
代理模式是一种结构型设计模式,主要用于为某个对象提供一个代理,以便在不直接访问对象的情况下控制对其的访问。代理可以在客户端和目标对象之间起到一个中介的作用,添加一些额外的操作,例如权限控制、性能优化(如延迟加载)、结果缓存、日志记录等。
通过代理模式,客户端与目标对象之间的耦合被降低,同时实现了对目标对象的透明化访问。
代理模式的特点
优点
控制对象访问:
- 可以通过代理控制对目标对象的访问,比如权限检查或延迟加载。
解耦增强系统灵活性:
- 客户端只需与代理对象交互,代理负责实际的操作逻辑,隐藏了具体的实现细节。
额外功能增强:
- 在不修改目标对象代码的前提下,通过代理扩展功能,例如添加日志记录、性能优化等。
缺点
增加系统复杂性:
- 引入代理对象会增加代码量和复杂性,尤其是代理逻辑较为复杂时。
性能开销:
- 代理本身需要额外的内存和处理时间。
代理模式的使用场景
权限控制(保护代理):
- 根据用户角色或权限控制对目标对象的访问。
- 例如:管理员可以查看银行账户余额,而普通用户不能。
延迟加载(虚拟代理):
- 在需要时才创建目标对象。
- 例如:大型资源(如图像、文件)在实际使用时再加载。
远程代理(Remote Proxy):
- 为位于不同地址空间的对象提供本地代理。
- 例如:远程方法调用(RMI)。
日志记录或审计:
- 在目标对象操作前后添加日志记录。
性能优化(缓存代理):
- 缓存目标对象的操作结果,避免重复计算或网络请求。
智能引用(Smart Reference Proxy):
- 在访问目标对象时进行计数、引用管理或预处理。
案例详解
以下通过两个案例详细解读代理模式:
- 虚拟代理(延迟加载)。
- 保护代理(权限控制)。
案例一:虚拟代理(延迟加载)
需求背景
假设我们要开发一个图片查看器应用。加载高清图片非常耗时,为了提升应用性能,我们可以使用代理模式,在需要时才加载图片。
代码实现
- 定义抽象接口
首先定义一个 Image
接口,供代理对象和目标对象实现。
#include <iostream>
#include <string>
#include <memory>
using namespace std;
// 抽象图片接口
class Image {
public:
virtual void display() = 0; // 定义显示图片的接口
virtual ~Image() = default;
};
- 实现真实对象
RealImage
是需要加载的高清图片,它模拟了一个耗时的加载过程。
class RealImage : public Image {
private:
string fileName;
void loadFromDisk() {
// 模拟加载图片
cout << "Loading image: " << fileName << endl;
}
public:
RealImage(const string& fileName) : fileName(fileName) {
loadFromDisk(); // 在创建时加载图片
}
void display() override {
cout << "Displaying image: " << fileName << endl;
}
};
- 实现代理对象
ProxyImage
是虚拟代理,只有在需要显示图片时,才创建 RealImage
。
class ProxyImage : public Image {
private:
string fileName;
unique_ptr<RealImage> realImage; // 延迟加载的真实图片对象
public:
ProxyImage(const string& fileName) : fileName(fileName) {}
void display() override {
if (!realImage) {
// 延迟加载真实图片
realImage = make_unique<RealImage>(fileName);
}
realImage->display();
}
};
- 客户端代码
客户端通过代理对象显示图片。
int main() {
// 创建代理图片
unique_ptr<Image> image = make_unique<ProxyImage>("test.jpg");
// 第一次显示图片
cout << "First time displaying image:" << endl;
image->display();
// 第二次显示图片
cout << "Second time displaying image:" << endl;
image->display();
return 0;
}
运行结果
First time displaying image:
Loading image: test.jpg
Displaying image: test.jpg
Second time displaying image:
Displaying image: test.jpg
解读
- 第一次显示时,
ProxyImage
延迟加载了RealImage
,并执行加载操作。 - 第二次显示时,直接使用已经加载好的图片,提升了性能。
案例二:保护代理(权限控制)
需求背景
实现一个银行账户系统,只有管理员用户可以查看账户余额,普通用户尝试查看时会被拒绝。
代码实现
- 定义接口
定义 BankAccount
接口,提供查看余额的功能。
class BankAccount {
public:
virtual void showBalance() = 0; // 查看余额方法
virtual ~BankAccount() = default;
};
- 实现真实对象
RealBankAccount
存储账户余额,并提供查看功能。
class RealBankAccount : public BankAccount {
private:
double balance;
public:
RealBankAccount(double initialBalance) : balance(initialBalance) {}
void showBalance() override {
cout << "The balance is: $" << balance << endl;
}
};
- 实现代理对象
BankAccountProxy
负责检查用户角色,只有管理员可以访问 RealBankAccount
。
class BankAccountProxy : public BankAccount {
private:
unique_ptr<RealBankAccount> realAccount;
string role; // 用户角色
public:
BankAccountProxy(double initialBalance, const string& userRole)
: realAccount(make_unique<RealBankAccount>(initialBalance)), role(userRole) {}
void showBalance() override {
if (role == "admin") {
realAccount->showBalance();
} else {
cout << "Access denied: Only admin can view the balance." << endl;
}
}
};
- 客户端代码
客户端通过代理访问账户信息。
int main() {
// 普通用户
unique_ptr<BankAccount> userAccount = make_unique<BankAccountProxy>(1000.0, "user");
userAccount->showBalance();
// 管理员
unique_ptr<BankAccount> adminAccount = make_unique<BankAccountProxy>(1000.0, "admin");
adminAccount->showBalance();
return 0;
}
运行结果
Access denied: Only admin can view the balance.
The balance is: $1000
解读
- 普通用户尝试查看余额时,代理拒绝访问。
- 管理员可以通过代理访问真实账户信息。
完整代码项目
文件结构
proxy_pattern_project/
│
├── main.cpp // 主程序入口
├── Image.h // 图片接口及相关类
├── BankAccount.h // 银行账户接口及相关类
1. Image.h
// Image.h
#ifndef IMAGE_H
#define IMAGE_H
#include <iostream>
#include <string>
#include <memory>
using namespace std;
// 抽象图片接口
class Image {
public:
virtual void display() = 0; // 定义显示图片的接口
virtual ~Image() = default;
};
// 真实图片类
class RealImage : public Image {
private:
string fileName;
void loadFromDisk() {
// 模拟加载图片的过程
cout << "加载图片:" << fileName << endl;
}
public:
RealImage(const string& fileName) : fileName(fileName) {
loadFromDisk(); // 构造时加载图片
}
void display() override {
cout << "显示图片:" << fileName << endl;
}
};
// 代理图片类
class ProxyImage : public Image {
private:
string fileName;
unique_ptr<RealImage> realImage; // 延迟加载的真实图片对象
public:
ProxyImage(const string& fileName) : fileName(fileName) {}
void display() override {
if (!realImage) {
// 延迟加载真实图片
realImage = make_unique<RealImage>(fileName);
}
realImage->display();
}
};
#endif // IMAGE_H
2. BankAccount.h
// BankAccount.h
#ifndef BANK_ACCOUNT_H
#define BANK_ACCOUNT_H
#include <iostream>
#include <memory>
#include <string>
using namespace std;
// 抽象银行账户接口
class BankAccount {
public:
virtual void showBalance() = 0; // 查看余额的方法
virtual ~BankAccount() = default;
};
// 真实银行账户类
class RealBankAccount : public BankAccount {
private:
double balance;
public:
RealBankAccount(double initialBalance) : balance(initialBalance) {}
void showBalance() override {
cout << "账户余额:$" << balance << endl;
}
};
// 银行账户代理类
class BankAccountProxy : public BankAccount {
private:
unique_ptr<RealBankAccount> realAccount;
string role; // 用户角色
public:
BankAccountProxy(double initialBalance, const string& userRole)
: realAccount(make_unique<RealBankAccount>(initialBalance)), role(userRole) {}
void showBalance() override {
if (role == "admin") {
realAccount->showBalance();
} else {
cout << "访问被拒绝:只有管理员可以查看余额。" << endl;
}
}
};
#endif // BANK_ACCOUNT_H
3. main.cpp
// main.cpp
#include "Image.h"
#include "BankAccount.h"
int main() {
cout << "====== 虚拟代理示例(延迟加载图片) ======" << endl;
// 创建代理图片对象
unique_ptr<Image> image = make_unique<ProxyImage>("test.jpg");
// 第一次显示图片(触发加载)
cout << "第一次显示图片:" << endl;
image->display();
// 第二次显示图片(直接显示已加载的图片)
cout << "第二次显示图片:" << endl;
image->display();
cout << "\n====== 保护代理示例(银行账户访问控制) ======" << endl;
// 普通用户访问
unique_ptr<BankAccount> userAccount = make_unique<BankAccountProxy>(1000.0, "user");
cout << "普通用户尝试查看余额:" << endl;
userAccount->showBalance();
// 管理员访问
unique_ptr<BankAccount> adminAccount = make_unique<BankAccountProxy>(1000.0, "admin");
cout << "管理员查看余额:" << endl;
adminAccount->showBalance();
return 0;
}
示例输出
虚拟代理示例
====== 虚拟代理示例(延迟加载图片) ======
第一次显示图片:
加载图片:test.jpg
显示图片:test.jpg
第二次显示图片:
显示图片:test.jpg
保护代理示例
====== 保护代理示例(银行账户访问控制) ======
普通用户尝试查看余额:
访问被拒绝:只有管理员可以查看余额。
管理员查看余额:
账户余额:$1000