设计模式(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 1Use 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 15. 第一个指针仍然有效: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网关、负载均衡器
掌握代理模式,意味着你不仅学会了一种设计模式,更学会了一种思考软件设计的方式——如何通过添加适当的间接层来创造更灵活、更高效、更安全的系统。
无论你是正在处理性能优化的前端工程师,还是构建分布式系统的后端架构师,代理模式都将是你工具箱中一件强大的武器。它优雅而强大,简单而深刻,真正体现了优秀软件设计的魅力。