设计模式(C++)详解——代理模式 (Proxy Pattern)(2)
<摘要>
代理模式是一种结构型设计模式,它就像一个聪明的"中间人"或"经纪人",在你和目标对象之间架起一座桥梁。这个中间人不仅帮你联系目标对象,还能帮你处理各种琐事:比如帮你筛选访问权限(保护代理)、帮你延迟加载大资源(虚拟代理)、甚至帮你与远方的对象沟通(远程代理)。最重要的是,这个中间人与目标对象长得一模一样(实现相同接口),让你几乎感觉不到它的存在,却能享受到各种增值服务。代理模式完美体现了"不要来找我,我会来找你"的软件设计哲学,让代码更加清晰、灵活和安全。从网页图片懒加载到银行ATM权限控制,从智能指针到分布式系统调用,代理模式无处不在,是现代软件开发中不可或缺的设计利器。
<解析>
1. 背景与核心概念:为什么我们需要"中间人"?
1.1 从现实世界到代码世界:代理的起源
想象一下这个场景:你是个大明星(真实对象),每天都有无数粉丝(客户端)想找你签名、合影。但如果你亲自处理所有请求,早就累垮了!于是你找了个经纪人(代理),所有请求都先经过他。经纪人会帮你:筛选合法的粉丝(权限控制)、安排合适的时间(延迟加载)、甚至代替你处理一些简单事务(缓存)。
这就是代理模式在现实世界的体现!在软件世界中,这种"经纪人"模式同样重要:
- 1994年:GoF(四人帮)在《设计模式》经典著作中首次系统化提出了代理模式
- 互联网时代:远程代理成为分布式系统的基础(如CORBA、Web Services)
- 现代开发:虚拟代理在Web应用(图片懒加载)、保护代理在安全系统、智能代理在资源管理中无处不在
1.2 核心概念三人组
代理模式有三个核心角色,它们的关系就像明星、经纪人和粉丝:
1. Subject(抽象主题) - “明星标准”
- 定义明星应该具备什么能力(接口)
- 无论是真明星还是经纪人,都必须符合这个标准
- 在代码中通常是抽象类或接口
2. RealSubject(真实主题) - “真明星”
- 真正有才华的对象,完成实际工作
- 但可能创建成本高、或者需要保护
- 比如:大型图像对象、数据库连接、核心业务逻辑
3. Proxy(代理) - “聪明经纪人”
- 也实现了"明星标准",所以能冒充真明星
- 持有或能创建RealSubject的引用
- 在调用真明星前后,能添加各种控制逻辑
1.3 代理家族的兄弟姐妹
代理模式有很多 specialized 的形式,就像经纪人也分不同类型:
| 代理类型 | 现实比喻 | 应用场景 |
|---|---|---|
| 虚拟代理 | 餐厅预订:只有客人到时才准备食物 | 网页图片懒加载、大数据延迟加载 |
| 保护代理 | 俱乐部保镖:检查会员资格 | 权限控制系统、访问控制列表 |
| 远程代理 | 国际快递:本地代表远程服务 | RPC、Web Services、分布式系统 |
| 智能代理 | 智能管家:自动处理日常事务 | 智能指针、自动垃圾回收 |
| 缓存代理 | 图书馆员:缓存常用书籍 | Web缓存、数据库查询缓存 |
2. 设计意图与考量:为什么选择代理模式?
2.1 核心目标:控制访问,而非增强功能
重要区别:代理模式 vs 装饰器模式
- 代理模式:控制访问(经纪人控制谁能见明星)
- 装饰器模式:增强功能(给明星化妆、换装)
代理模式的核心意图是在不直接访问对象的情况下,控制对它的访问。这带来了四大好处:
- 懒加载优化:对于创建开销大的对象,直到真正需要时才创建
- 访问控制:添加权限验证,保护敏感对象
- 远程访问简化:让远程调用像本地调用一样简单
- 智能管理:自动处理内存、缓存等基础事务
2.2 背后的设计哲学
代理模式体现了这些优秀设计原则:
- 开闭原则:通过添加代理来扩展功能,而不是修改现有代码
- 单一职责:RealSubject专注业务逻辑,Proxy专注访问控制
- 最少知识:客户端只与抽象接口交互,降低耦合度
2.3 权衡利弊:不是银弹
优点:
- 🚀 性能提升:通过懒加载和缓存减少不必要的开销
- 🛡️ 安全性增强:添加额外的安全层
- 🔧 架构清晰:分离核心逻辑与辅助逻辑
缺点:
- 🐢 轻微性能损耗:多一层调用,微秒级的延迟
- 🧠 复杂度增加:需要编写额外的代理类
- 🔄 可能过度设计:简单场景不需要代理
3. 实例与应用场景:看看代理模式在实际中如何工作
让我们通过几个生动的例子,看看代理模式如何解决实际问题。
案例一:虚拟代理 - 网页图片懒加载
场景:一个旅游博客页面有20张高清大图(每张5MB)。如果一次性加载所有图片,用户会等到花儿都谢了!
解决方案:使用图片代理实现懒加载——只有图片进入可视区域时才加载真实图片。
#include <iostream>
#include <string>
#include <vector>
// 1. 抽象图片接口
class Image {
public:
virtual ~Image() = default;
virtual void display() = 0;
virtual int getWidth() const = 0;
virtual int getHeight() const = 0;
};
// 2. 真实图片 - 创建成本高
class HighResImage : public Image {
public:
HighResImage(const std::string& path) : m_path(path) {
loadFromDisk(); // 构造函数中就加载,很耗时!
}
void display() override {
std::cout << "Displaying high-res image: " << m_path << std::endl;
}
int getWidth() const override { return 1920; }
int getHeight() const override { return 1080; }
private:
void loadFromDisk() {
std::cout << "Loading large image from disk: " << m_path
<< " (This takes time and memory!)" << std::endl;
// 模拟耗时操作
for (int i = 0; i < 100000000; i++) {} // 空循环模拟耗时
}
std::string m_path;
};
// 3. 图片代理 - 实现懒加载
class LazyImageProxy : public Image {
public:
LazyImageProxy(const std::string& path)
: m_path(path), m_realImage(nullptr), m_width(1920), m_height(1080) {
std::cout << "Proxy created for: " << path << " (No heavy loading yet)" << std::endl;
}
~LazyImageProxy() {
delete m_realImage;
}
void display() override {
if (m_realImage == nullptr) {
std::cout << "User scrolled to image. Now loading..." << std::endl;
m_realImage = new HighResImage(m_path);
}
m_realImage->display();
}
int getWidth() const override { return m_width; }
int getHeight() const override { return m_height; }
// 一些代理特有的方法
bool isLoaded() const { return m_realImage != nullptr; }
private:
std::string m_path;
HighResImage* m_realImage;
int m_width, m_height;
};
// 客户端代码 - 模拟网页滚动
int main() {
std::cout << "=== 网页加载中... ===" << std::endl;
// 创建多个图片代理 - 瞬间完成!
std::vector<LazyImageProxy> images;
images.emplace_back("alps_mountain.jpg");
images.emplace_back("santorini_sunset.jpg");
images.emplace_back("tokyo_cityscape.jpg");
std::cout << "\n所有图片代理已创建,网页显示占位符" << std::endl;
std::cout << "用户开始滚动页面...\n" << std::endl;
// 模拟用户滚动到第二张图片
std::cout << "--- 用户滚动到第二张图片 ---" << std::endl;
images[1].display();
// 模拟用户继续滚动到第三张图片
std::cout << "\n--- 用户继续滚动到第三张图片 ---" << std::endl;
images[2].display();
// 模拟用户回滚到第二张图片
std::cout << "\n--- 用户回滚到第二张图片 ---" << std::endl;
images[1].display(); // 这次已经加载过了
std::cout << "\n=== 用户体验:流畅快速! ===" << std::endl;
return 0;
}
输出结果:
=== 网页加载中... ===
Proxy created for: alps_mountain.jpg (No heavy loading yet)
Proxy created for: santorini_sunset.jpg (No heavy loading yet)
Proxy created for: tokyo_cityscape.jpg (No heavy loading yet)
所有图片代理已创建,网页显示占位符
用户开始滚动页面...
--- 用户滚动到第二张图片 ---
User scrolled to image. Now loading...
Loading large image from disk: santorini_sunset.jpg (This takes time and memory!)
Displaying high-res image: santorini_sunset.jpg
--- 用户继续滚动到第三张图片 ---
User scrolled to image. Now loading...
Loading large image from disk: tokyo_cityscape.jpg (This takes time and memory!)
Displaying high-res image: tokyo_cityscape.jpg
--- 用户回滚到第二张图片 ---
Displaying high-res image: santorini_sunset.jpg
=== 用户体验:流畅快速! ===
时序图:
案例二:保护代理 -银行账户权限控制
场景:银行系统中,不同用户对账户有不同的操作权限。普通用户只能查询余额,经理可以修改账户信息,只有特定权限的用户才能进行转账操作。
解决方案:使用保护代理来控制对银行账户对象的访问。
#include <iostream>
#include <string>
#include <memory>
// 用户角色枚举
enum class UserRole { CUSTOMER, TELLER, MANAGER, AUDITOR };
// 1. 抽象银行账户接口
class BankAccount {
public:
virtual ~BankAccount() = default;
virtual double getBalance() const = 0;
virtual void withdraw(double amount) = 0;
virtual void deposit(double amount) = 0;
virtual void transfer(const std::string& toAccount, double amount) = 0;
virtual void updateProfile(const std::string& newProfile) = 0;
virtual const std::string& getAccountNumber() const = 0;
};
// 2. 真实银行账户
class RealBankAccount : public BankAccount {
public:
RealBankAccount(const std::string& accNumber, double initialBalance)
: m_accountNumber(accNumber), m_balance(initialBalance) {}
double getBalance() const override {
return m_balance;
}
void withdraw(double amount) override {
if (amount <= m_balance) {
m_balance -= amount;
std::cout << "Withdrew $" << amount << ". New balance: $" << m_balance << std::endl;
} else {
std::cout << "Insufficient funds!" << std::endl;
}
}
void deposit(double amount) override {
m_balance += amount;
std::cout << "Deposited $" << amount << ". New balance: $" << m_balance << std::endl;
}
void transfer(const std::string& toAccount, double amount) override {
if (amount <= m_balance) {
m_balance -= amount;
std::cout << "Transferred $" << amount << " to account " << toAccount
<< ". New balance: $" << m_balance << std::endl;
} else {
std::cout << "Insufficient funds for transfer!" << std::endl;
}
}
void updateProfile(const std::string& newProfile) override {
m_profile = newProfile;
std::cout << "Profile updated successfully." << std::endl;
}
const std::string& getAccountNumber() const override {
return m_accountNumber;
}
private:
std::string m_accountNumber;
double m_balance;
std::string m_profile;
};
// 3. 保护代理
class BankAccountProxy : public BankAccount {
public:
BankAccountProxy(const std::string& accNumber, double initialBalance, UserRole role)
: m_realAccount(std::make_shared<RealBankAccount>(accNumber, initialBalance)),
m_userRole(role) {}
double getBalance() const override {
std::cout << "[Proxy] Balance check requested..." << std::endl;
// 任何人都可以查询余额
return m_realAccount->getBalance();
}
void withdraw(double amount) override {
std::cout << "[Proxy] Withdrawal requested..." << std::endl;
// 只有柜员、经理和客户本人可以取款
if (m_userRole == UserRole::CUSTOMER || m_userRole == UserRole::TELLER ||
m_userRole == UserRole::MANAGER) {
m_realAccount->withdraw(amount);
} else {
std::cout << "Access denied: Insufficient privileges for withdrawal." << std::endl;
}
}
void deposit(double amount) override {
std::cout << "[Proxy] Deposit requested..." << std::endl;
// 只有柜员、经理和客户本人可以存款
if (m_userRole == UserRole::CUSTOMER || m_userRole == UserRole::TELLER ||
m_userRole == UserRole::MANAGER) {
m_realAccount->deposit(amount);
} else {
std::cout << "Access denied: Insufficient privileges for deposit." << std::endl;
}
}
void transfer(const std::string& toAccount, double amount) override {
std::cout << "[Proxy] Transfer requested..." << std::endl;
// 只有经理可以进行转账
if (m_userRole == UserRole::MANAGER) {
m_realAccount->transfer(toAccount, amount);
} else {
std::cout << "Access denied: Only managers can perform transfers." << std::endl;
}
}
void updateProfile(const std::string& newProfile) override {
std::cout << "[Proxy] Profile update requested..." << std::endl;
// 只有经理和审计员可以修改资料
if (m_userRole == UserRole::MANAGER || m_userRole == UserRole::AUDITOR) {
m_realAccount->updateProfile(newProfile);
} else {
std::cout << "Access denied: Insufficient privileges for profile update." << std::endl;
}
}
const std::string& getAccountNumber() const override {
return m_realAccount->getAccountNumber();
}
private:
std::shared_ptr<RealBankAccount> m_realAccount;
UserRole m_userRole;
};
// 客户端代码
int main() {
std::cout << "=== 银行账户权限控制系统 ===\n" << std::endl;
// 创建不同角色的用户代理
BankAccountProxy customerAccount("123456789", 5000.0, UserRole::CUSTOMER);
BankAccountProxy tellerAccount("123456789", 5000.0, UserRole::TELLER);
BankAccountProxy managerAccount("123456789", 5000.0, UserRole::MANAGER);
BankAccountProxy auditorAccount("123456789", 5000.0, UserRole::AUDITOR);
std::cout << "1. 客户尝试操作自己的账户:" << std::endl;
std::cout << " Balance: $" << customerAccount.getBalance() << std::endl;
customerAccount.withdraw(100.0);
customerAccount.transfer("987654321", 200.0); // 应该失败
std::cout << "\n2. 柜员尝试操作账户:" << std::endl;
tellerAccount.deposit(500.0);
tellerAccount.updateProfile("New address"); // 应该失败
std::cout << "\n3. 经理尝试操作账户:" << std::endl;
managerAccount.transfer("987654321", 200.0); // 应该成功
managerAccount.updateProfile("New contact info");
std::cout << "\n4. 审计员尝试操作账户:" << std::endl;
std::cout << " Balance: $" << auditorAccount.getBalance() << std::endl;
auditorAccount.updateProfile("Auditor notes");
auditorAccount.withdraw(50.0); // 应该失败
return 0;
}
输出结果:
=== 银行账户权限控制系统 ===
1. 客户尝试操作自己的账户:
[Proxy] Balance check requested...
Balance: $5000
[Proxy] Withdrawal requested...
Withdrew $100. New balance: $4900
[Proxy] Transfer requested...
Access denied: Only managers can perform transfers.
2. 柜员尝试操作账户:
[Proxy] Deposit requested...
Deposited $500. New balance: $5400
[Proxy] Profile update requested...
Access denied: Insufficient privileges for profile update.
3. 经理尝试操作账户:
[Proxy] Transfer requested...
Transferred $200 to account 987654321. New balance: $5200
[Proxy] Profile update requested...
Profile updated successfully.
4. 审计员尝试操作账户:
[Proxy] Balance check requested...
Balance: $5200
[Proxy] Profile update requested...
Profile updated successfully.
[Proxy] Withdrawal requested...
Access denied: Insufficient privileges for withdrawal.
这个保护代理就像银行的智能门禁系统,根据每个人的身份卡(UserRole)决定允许进行的操作,既方便了合法操作,又保护了账户安全。
案例三:智能指针 - C++中的资源管理代理
场景:在C++中,手动管理内存(new/delete)容易导致内存泄漏和悬空指针。我们需要一个"智能"的代理来帮我们自动管理资源生命周期。
解决方案:智能指针(如std::shared_ptr)作为资源对象的代理,通过引用计数自动管理内存。
#include <iostream>
#include <memory>
// 一个昂贵的资源类
class ExpensiveResource {
public:
ExpensiveResource(int id) : m_id(id) {
std::cout << "ExpensiveResource " << m_id << " created." << std::endl;
}
~ExpensiveResource() {
std::cout << "ExpensiveResource " << m_id << " destroyed." << std::endl;
}
void doWork() {
std::cout << "ExpensiveResource " << m_id << " is working." << std::endl;
}
int getId() const { return m_id; }
private:
int m_id;
};
// 简化的智能指针代理(模拟shared_ptr)
template<typename T>
class SmartPtr {
public:
// 构造函数 - 获取资源
explicit SmartPtr(T* ptr = nullptr)
: m_ptr(ptr), m_refCount(new int(1)) {
std::cout << "SmartPtr created. Ref count: " << *m_refCount
<< " for resource " << (ptr ? ptr->getId() : -1) << std::endl;
}
// 拷贝构造函数 - 共享资源
SmartPtr(const SmartPtr<T>& other)
: m_ptr(other.m_ptr), m_refCount(other.m_refCount) {
(*m_refCount)++;
std::cout << "SmartPtr copied. Ref count: " << *m_refCount
<< " for resource " << (m_ptr ? m_ptr->getId() : -1) << std::endl;
}
// 析构函数 - 释放资源
~SmartPtr() {
(*m_refCount)--;
std::cout << "SmartPtr destructor. Ref count: " << *m_refCount
<< " for resource " << (m_ptr ? m_ptr->getId() : -1) << std::endl;
if (*m_refCount == 0) {
delete m_ptr;
delete m_refCount;
std::cout << "Resource freed." << std::endl;
}
}
// 重载运算符,让代理用起来像原始指针
T* operator->() const { return m_ptr; }
T& operator*() const { return *m_ptr; }
// 获取引用计数
int useCount() const { return *m_refCount; }
private:
T* m_ptr; // 被代理的原始指针
int* m_refCount; // 引用计数
};
// 客户端代码
int main() {
std::cout << "=== 智能指针演示 ===\n" << std::endl;
std::cout << "1. 创建第一个智能指针:" << std::endl;
SmartPtr<ExpensiveResource> ptr1(new ExpensiveResource(1));
ptr1->doWork();
std::cout << "\n2. 拷贝构造第二个智能指针(共享资源):" << std::endl;
{
SmartPtr<ExpensiveResource> ptr2 = ptr1;
std::cout << " Use count: " << ptr2.useCount() << std::endl;
ptr2->doWork();
std::cout << "\n3. 创建第三个智能指针(另一个资源):" << std::endl;
SmartPtr<ExpensiveResource> ptr3(new ExpensiveResource(2));
ptr3->doWork();
std::cout << "\n4. 离开内部作用域:" << std::endl;
} // ptr2和ptr3析构
std::cout << "\n5. 第一个指针仍然有效:" << std::endl;
std::cout << " Use count: " << ptr1.useCount() << std::endl;
ptr1->doWork();
std::cout << "\n6. 离开main函数,所有资源自动释放:" << std::endl;
return 0;
}
输出结果:
=== 智能指针演示 ===
1. 创建第一个智能指针:
ExpensiveResource 1 created.
SmartPtr created. Ref count: 1 for resource 1
ExpensiveResource 1 is working.
2. 拷贝构造第二个智能指针(共享资源):
SmartPtr copied. Ref count: 2 for resource 1
Use count: 2
ExpensiveResource 1 is working.
3. 创建第三个智能指针(另一个资源):
ExpensiveResource 2 created.
SmartPtr created. Ref count: 1 for resource 2
ExpensiveResource 2 is working.
4. 离开内部作用域:
SmartPtr destructor. Ref count: 1 for resource 2
Resource freed.
ExpensiveResource 2 destroyed.
SmartPtr destructor. Ref count: 1 for resource 1
5. 第一个指针仍然有效:
Use count: 1
ExpensiveResource 1 is working.
6. 离开main函数,所有资源自动释放:
SmartPtr destructor. Ref count: 0 for resource 1
Resource freed.
ExpensiveResource 1 destroyed.
智能指针就像一个负责任的图书管理员:
- 借出书籍(创建资源):记录谁借了书
- 多人借阅(拷贝共享):增加借阅计数
- 归还书籍(析构释放):减少借阅计数,当没人借阅时把书放回书架(释放资源)
这样就不再需要手动记住何时删除资源,大大减少了内存泄漏的风险!
4. 完整代码实现与编译运行
让我们为虚拟代理案例提供一个完整的、工程化的实现,包括详细的注释和Makefile。
4.1 完整注释的代码实现
image_proxy.h
/**
* @file image_proxy.h
* @brief 虚拟代理模式示例:图像延迟加载
*
* 实现了一个高性能的图像加载系统,通过代理模式实现图像的延迟加载,
* 显著提升应用程序启动速度和内存使用效率。
*/
#ifndef IMAGE_PROXY_H
#define IMAGE_PROXY_H
#include <iostream>
#include <string>
#include <memory>
/**
* @brief 抽象图像接口 (Subject)
*
* 定义所有图像对象必须实现的接口,确保真实图像和代理图像可以互换使用。
* 这是代理模式能够透明工作的基础。
*/
class IImage {
public:
virtual ~IImage() = default;
/**
* @brief 显示图像
*
* 将图像内容渲染到显示设备上。具体实现可能涉及实际的图形渲染或简单的控制台输出。
*/
virtual void display() = 0;
/**
* @brief 获取图像宽度
* @return 图像的像素宽度
*/
virtual int getWidth() const = 0;
/**
* @brief 获取图像高度
* @return 图像的像素高度
*/
virtual int getHeight() const = 0;
/**
* @brief 获取图像文件名
* @return 图像的文件路径
*/
virtual std::string getFilename() const = 0;
};
/**
* @brief 高分辨率图像类 (RealSubject)
*
* 代表实际的高分辨率图像对象,构造时需要从磁盘加载图像数据,
* 这是一个昂贵操作,会消耗大量时间和内存。
*/
class HighResolutionImage : public IImage {
public:
/**
* @brief 构造函数
* @param filename 图像文件路径
*
* 创建HighResolutionImage对象并立即从磁盘加载图像数据。
* 注意:这个操作非常昂贵,应该在真正需要时才执行。
*/
explicit HighResolutionImage(const std::string& filename);
void display() override;
int getWidth() const override;
int getHeight() const override;
std::string getFilename() const override;
private:
/**
* @brief 从磁盘加载图像数据
*
* 模拟从文件系统加载高分辨率图像的实际操作。
* 这是一个耗时且资源密集型的I/O操作。
*/
void loadFromDisk();
std::string m_filename; ///< 图像文件路径
int m_width; ///< 图像宽度(像素)
int m_height; ///< 图像高度(像素)
// 实际项目中这里会有图像像素数据等成员
};
/**
* @brief 图像代理类 (Proxy)
*
* 作为HighResolutionImage的代理,控制对其的访问。
* 实现延迟加载模式:仅在第一次真正需要显示图像时才创建真实的图像对象。
*/
class LazyImageProxy : public IImage {
public:
/**
* @brief 构造函数
* @param filename 图像文件路径
*
* 创建图像代理对象,但不立即加载真实图像。
* 这个操作非常快速,只存储图像的基本元数据。
*/
explicit LazyImageProxy(const std::string& filename);
/**
* @brief 析构函数
*
* 确保在代理销毁时正确释放管理的真实图像资源。
*/
~LazyImageProxy();
void display() override;
int getWidth() const override;
int getHeight() const override;
std::string getFilename() const override;
/**
* @brief 检查真实图像是否已加载
* @return true如果真实图像已加载,false否则
*/
bool isLoaded() const;
private:
std::string m_filename; ///< 图像文件路径
int m_width; ///< 图像宽度(代理已知的元数据)
int m_height; ///< 图像高度(代理已知的元数据)
HighResolutionImage* m_realImage; ///< 指向真实图像的指针(延迟初始化)
/**
* @brief 确保真实图像已加载
*
* 如果真实图像尚未加载,则创建并加载它。
* 这是一个惰性初始化方法。
*/
void ensureImageLoaded();
};
#endif // IMAGE_PROXY_H
image_proxy.cpp
/**
* @file image_proxy.cpp
* @brief 虚拟代理模式示例的实现文件
*/
#include "image_proxy.h"
#include <thread> // 用于模拟耗时操作
#include <chrono>
// HighResolutionImage 实现
HighResolutionImage::HighResolutionImage(const std::string& filename)
: m_filename(filename), m_width(1920), m_height(1080) {
loadFromDisk();
}
void HighResolutionImage::display() {
std::cout << "Displaying high-resolution image: " << m_filename << std::endl;
// 实际项目中这里会有实际的图像渲染代码
}
int HighResolutionImage::getWidth() const {
return m_width;
}
int HighResolutionImage::getHeight() const {
return m_height;
}
std::string HighResolutionImage::getFilename() const {
return m_filename;
}
void HighResolutionImage::loadFromDisk() {
std::cout << "Loading high-resolution image from disk: " << m_filename << std::endl;
std::cout << "This operation is expensive and time-consuming..." << std::endl;
// 模拟耗时的磁盘I/O操作
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Image loaded successfully: " << m_filename << std::endl;
// 实际项目中这里会有实际的图像加载代码
}
// LazyImageProxy 实现
LazyImageProxy::LazyImageProxy(const std::string& filename)
: m_filename(filename), m_width(1920), m_height(1080), m_realImage(nullptr) {
std::cout << "Proxy created for image: " << filename << " (Real image not loaded yet)" << std::endl;
}
LazyImageProxy::~LazyImageProxy() {
delete m_realImage; // 安全删除(delete nullptr是安全的)
}
void LazyImageProxy::display() {
ensureImageLoaded();
m_realImage->display();
}
int LazyImageProxy::getWidth() const {
return m_width;
}
int LazyImageProxy::getHeight() const {
return m_height;
}
std::string LazyImageProxy::getFilename() const {
return m_filename;
}
bool LazyImageProxy::isLoaded() const {
return m_realImage != nullptr;
}
void LazyImageProxy::ensureImageLoaded() {
if (m_realImage == nullptr) {
std::cout << "Lazy loading triggered for image: " << m_filename << std::endl;
m_realImage = new HighResolutionImage(m_filename);
}
}
main.cpp
/**
* @file main.cpp
* @brief 客户端代码,演示虚拟代理的使用
*
* 模拟一个图像查看器应用程序,展示如何使用虚拟代理实现延迟加载,
* 提升应用程序的启动性能和响应速度。
*/
#include "image_proxy.h"
#include <iostream>
#include <vector>
#include <memory>
/**
* @brief 模拟图像查看器应用程序
*
* 创建一个包含多个高分辨率图像的画廊,但使用代理实现延迟加载,
* 只有在用户真正查看某个图像时才加载它。
*/
int main() {
std::cout << "=== 图像查看器应用程序启动 ===" << std::endl;
std::cout << "创建图像代理列表..." << std::endl;
// 使用智能指针管理代理对象
std::vector<std::unique_ptr<IImage>> imageGallery;
// 添加多个图像代理 - 瞬间完成!
imageGallery.emplace_back(std::make_unique<LazyImageProxy>("nature_landscape.jpg"));
imageGallery.emplace_back(std::make_unique<LazyImageProxy>("city_skyline.jpg"));
imageGallery.emplace_back(std::make_unique<LazyImageProxy>("mountain_view.jpg"));
imageGallery.emplace_back(std::make_unique<LazyImageProxy>("beach_sunset.jpg"));
imageGallery.emplace_back(std::make_unique<LazyImageProxy>("forest_path.jpg"));
std::cout << "\n所有图像代理已创建,应用程序已就绪" << std::endl;
std::cout << "应用程序启动时间:几乎为零!" << std::endl;
std::cout << "内存占用:极小(只存储元数据)" << std::endl;
std::cout << "\n--- 用户浏览到第二张图像 ---" << std::endl;
imageGallery[1]->display();
std::cout << "\n--- 用户继续浏览到第五张图像 ---" << std::endl;
imageGallery[4]->display();
std::cout << "\n--- 用户返回到第二张图像 ---" << std::endl;
imageGallery[1]->display(); // 这次已经加载过了
std::cout << "\n=== 应用程序使用统计 ===" << std::endl;
std::cout << "总图像数量: " << imageGallery.size() << std::endl;
std::cout << "已加载图像数量: ";
int loadedCount = 0;
for (const auto& image : imageGallery) {
// 动态类型检查,在实际项目中可能有更好的方法
if (auto proxy = dynamic_cast<LazyImageProxy*>(image.get())) {
if (proxy->isLoaded()) {
loadedCount++;
}
}
}
std::cout << loadedCount << " / " << imageGallery.size() << std::endl;
std::cout << "内存使用优化: "
<< (100 - (loadedCount * 100 / imageGallery.size()))
<< "%" << std::endl;
std::cout << "\n=== 应用程序退出 ===" << std::endl;
return 0;
}
4.2 Makefile 与编译运行
Makefile
# 图像代理演示项目的Makefile
# 编译器设置
CXX := g++
CXXFLAGS := -std=c++17 -Wall -Wextra -O2 -pthread
# 目标文件设置
TARGET := image_proxy_demo
SRCS := main.cpp image_proxy.cpp
OBJS := $(SRCS:.cpp=.o)
HEADERS := image_proxy.h
.PHONY: all clean run
# 默认目标
all: $(TARGET)
# 链接可执行文件
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) -o $@ $^
# 编译源文件
%.o: %.cpp $(HEADERS)
$(CXX) $(CXXFLAGS) -c $< -o $@
# 清理生成的文件
clean:
rm -f $(TARGET) $(OBJS)
# 运行程序
run: $(TARGET)
./$(TARGET)
# 调试构建
debug: CXXFLAGS += -g -DDEBUG
debug: clean $(TARGET)
# 显示帮助信息
help:
@echo "可用目标:"
@echo " all 编译项目(默认)"
@echo " clean 清理生成的文件"
@echo " run 编译并运行程序"
@echo " debug 创建调试版本"
@echo " help 显示此帮助信息"
编译与运行步骤:
-
保存文件:将上述代码保存到相应文件中
image_proxy.h- 头文件image_proxy.cpp- 实现文件main.cpp- 主程序Makefile- 构建脚本
-
编译项目:
make或者明确指定:
make all -
运行程序:
make run或者直接运行:
./image_proxy_demo -
清理项目:
make clean -
创建调试版本:
make debug
预期输出:
程序会模拟一个图像查看器的启动和用户交互过程,展示代理模式如何实现延迟加载。你会看到:
- 应用程序瞬间启动,只创建代理对象
- 当用户查看特定图片时,才真正加载该图片
- 再次查看已加载图片时,瞬间显示
- 最后显示内存使用统计,证明代理模式的有效性
这个完整的示例展示了代理模式在实际项目中的应用,包括工程化的代码组织、详细的文档注释和自动化构建过程。
5. 代理模式在复杂系统中的高级应用
5.1 远程代理:跨越网络边界的透明调用
远程代理是分布式系统中的基础模式,它让客户端能够像调用本地对象一样调用远程对象。现代微服务架构中的服务网关、API代理等都是这一思想的延伸。
工作原理时序图:
5.2 动态代理:运行时创建的代理
在一些高级语言(如Java、C#)中,支持动态代理机制,可以在运行时动态创建代理类,而不需要手动编写代理类代码。这大大简化了代理模式的应用。
动态代理的优势:
- 减少样板代码:不需要为每个类编写具体的代理类
- 更易维护:通用代理逻辑可以集中处理
- 更灵活:可以在运行时决定代理行为
6. 总结:代理模式的智慧
代理模式不仅仅是一种技术实现,更是一种设计哲学。它教会我们:
- 间接性的价值:通过添加适当的间接层,可以解决许多直接访问带来的问题
- 关注点分离:让每个对象专注于自己的核心职责,代理处理访问控制,真实对象处理业务逻辑
- 透明性的力量:好的设计应该对使用者透明,不需要改变使用方式就能获得额外好处
- 延迟的艺术:有时候,推迟做事情(延迟加载)比立即做所有事情更明智
在现代软件开发中,代理模式的应用比想象中更加广泛:
- 前端开发:图片懒加载、API请求代理
- 后端开发:数据库连接池、缓存代理、服务网关
- 系统编程:智能指针、系统调用代理
- 分布式系统:服务网格、API网关、负载均衡器
掌握代理模式,意味着你不仅学会了一种设计模式,更学会了一种思考软件设计的方式——如何通过添加适当的间接层来创造更灵活、更高效、更安全的系统。
无论你是正在处理性能优化的前端工程师,还是构建分布式系统的后端架构师,代理模式都将是你工具箱中一件强大的武器。它优雅而强大,简单而深刻,真正体现了优秀软件设计的魅力。
更多推荐


所有评论(0)