当前位置: 首页 > news >正文

设计模式(C++)详解——代理模式 (Proxy Pattern)(2)

<摘要>

代理模式是一种结构型设计模式,它就像一个聪明的"中间人"或"经纪人",在你和目标对象之间架起一座桥梁。这个中间人不仅帮你联系目标对象,还能帮你处理各种琐事:比如帮你筛选访问权限(保护代理)、帮你延迟加载大资源(虚拟代理)、甚至帮你与远方的对象沟通(远程代理)。最重要的是,这个中间人与目标对象长得一模一样(实现相同接口),让你几乎感觉不到它的存在,却能享受到各种增值服务。代理模式完美体现了"不要来找我,我会来找你"的软件设计哲学,让代码更加清晰、灵活和安全。从网页图片懒加载到银行ATM权限控制,从智能指针到分布式系统调用,代理模式无处不在,是现代软件开发中不可或缺的设计利器。


<解析>

1. 背景与核心概念:为什么我们需要"中间人"?

1.1 从现实世界到代码世界:代理的起源

想象一下这个场景:你是个大明星(真实对象),每天都有无数粉丝(客户端)想找你签名、合影。但如果你亲自处理所有请求,早就累垮了!于是你找了个经纪人(代理),所有请求都先经过他。经纪人会帮你:筛选合法的粉丝(权限控制)、安排合适的时间(延迟加载)、甚至代替你处理一些简单事务(缓存)。

这就是代理模式在现实世界的体现!在软件世界中,这种"经纪人"模式同样重要:

  • 1994年:GoF(四人帮)在《设计模式》经典著作中首次系统化提出了代理模式
  • 互联网时代:远程代理成为分布式系统的基础(如CORBA、Web Services)
  • 现代开发:虚拟代理在Web应用(图片懒加载)、保护代理在安全系统、智能代理在资源管理中无处不在
1.2 核心概念三人组

代理模式有三个核心角色,它们的关系就像明星、经纪人和粉丝:

controls access to
«interface»
ISubject
+request()
RealSubject
+request()
Proxy
-realSubject: RealSubject
+request()

1. Subject(抽象主题) - “明星标准”

  • 定义明星应该具备什么能力(接口)
  • 无论是真明星还是经纪人,都必须符合这个标准
  • 在代码中通常是抽象类或接口

2. RealSubject(真实主题) - “真明星”

  • 真正有才华的对象,完成实际工作
  • 但可能创建成本高、或者需要保护
  • 比如:大型图像对象、数据库连接、核心业务逻辑

3. Proxy(代理) - “聪明经纪人”

  • 也实现了"明星标准",所以能冒充真明星
  • 持有或能创建RealSubject的引用
  • 在调用真明星前后,能添加各种控制逻辑
1.3 代理家族的兄弟姐妹

代理模式有很多 specialized 的形式,就像经纪人也分不同类型:

代理类型现实比喻应用场景
虚拟代理餐厅预订:只有客人到时才准备食物网页图片懒加载、大数据延迟加载
保护代理俱乐部保镖:检查会员资格权限控制系统、访问控制列表
远程代理国际快递:本地代表远程服务RPC、Web Services、分布式系统
智能代理智能管家:自动处理日常事务智能指针、自动垃圾回收
缓存代理图书馆员:缓存常用书籍Web缓存、数据库查询缓存

2. 设计意图与考量:为什么选择代理模式?

2.1 核心目标:控制访问,而非增强功能

重要区别:代理模式 vs 装饰器模式

  • 代理模式:控制访问(经纪人控制谁能见明星)
  • 装饰器模式:增强功能(给明星化妆、换装)

代理模式的核心意图是在不直接访问对象的情况下,控制对它的访问。这带来了四大好处:

  1. 懒加载优化:对于创建开销大的对象,直到真正需要时才创建
  2. 访问控制:添加权限验证,保护敏感对象
  3. 远程访问简化:让远程调用像本地调用一样简单
  4. 智能管理:自动处理内存、缓存等基础事务
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=== 用户体验:流畅快速! ===

时序图

UserBrowserImage ProxyRealImage滚动页面display()第一次请求:创建RealImagenew HighResImage()display()显示图片看到图片再次滚动到同一图片display()已存在:直接使用display()显示图片看到图片(瞬间加载)UserBrowserImage ProxyRealImage
案例二:保护代理 -银行账户权限控制

场景:银行系统中,不同用户对账户有不同的操作权限。普通用户只能查询余额,经理可以修改账户信息,只有特定权限的用户才能进行转账操作。

解决方案:使用保护代理来控制对银行账户对象的访问。

#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.

智能指针就像一个负责任的图书管理员:

  1. 借出书籍(创建资源):记录谁借了书
  2. 多人借阅(拷贝共享):增加借阅计数
  3. 归还书籍(析构释放):减少借阅计数,当没人借阅时把书放回书架(释放资源)

这样就不再需要手动记住何时删除资源,大大减少了内存泄漏的风险!

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   显示此帮助信息"

编译与运行步骤

  1. 保存文件:将上述代码保存到相应文件中

    • image_proxy.h - 头文件
    • image_proxy.cpp - 实现文件
    • main.cpp - 主程序
    • Makefile - 构建脚本
  2. 编译项目

    make
    

    或者明确指定:

    make all
    
  3. 运行程序

    make run
    

    或者直接运行:

    ./image_proxy_demo
    
  4. 清理项目

    make clean
    
  5. 创建调试版本

    make debug
    

预期输出
程序会模拟一个图像查看器的启动和用户交互过程,展示代理模式如何实现延迟加载。你会看到:

  1. 应用程序瞬间启动,只创建代理对象
  2. 当用户查看特定图片时,才真正加载该图片
  3. 再次查看已加载图片时,瞬间显示
  4. 最后显示内存使用统计,证明代理模式的有效性

这个完整的示例展示了代理模式在实际项目中的应用,包括工程化的代码组织、详细的文档注释和自动化构建过程。

5. 代理模式在复杂系统中的高级应用

5.1 远程代理:跨越网络边界的透明调用

远程代理是分布式系统中的基础模式,它让客户端能够像调用本地对象一样调用远程对象。现代微服务架构中的服务网关、API代理等都是这一思想的延伸。

工作原理时序图

ClientClient Stub (Proxy)NetworkSkeletonRemote Service调用准备阶段call remoteMethod(params)1. 序列化参数2. 创建请求消息发送请求消息网络传输接收请求消息3. 反序列化参数remoteMethod(params)执行与返回执行实际业务逻辑返回结果4. 序列化结果发送响应消息响应返回接收响应消息5. 反序列化结果返回结果ClientClient Stub (Proxy)NetworkSkeletonRemote Service
5.2 动态代理:运行时创建的代理

在一些高级语言(如Java、C#)中,支持动态代理机制,可以在运行时动态创建代理类,而不需要手动编写代理类代码。这大大简化了代理模式的应用。

动态代理的优势

  • 减少样板代码:不需要为每个类编写具体的代理类
  • 更易维护:通用代理逻辑可以集中处理
  • 更灵活:可以在运行时决定代理行为

6. 总结:代理模式的智慧

代理模式不仅仅是一种技术实现,更是一种设计哲学。它教会我们:

  1. 间接性的价值:通过添加适当的间接层,可以解决许多直接访问带来的问题
  2. 关注点分离:让每个对象专注于自己的核心职责,代理处理访问控制,真实对象处理业务逻辑
  3. 透明性的力量:好的设计应该对使用者透明,不需要改变使用方式就能获得额外好处
  4. 延迟的艺术:有时候,推迟做事情(延迟加载)比立即做所有事情更明智

在现代软件开发中,代理模式的应用比想象中更加广泛:

  • 前端开发:图片懒加载、API请求代理
  • 后端开发:数据库连接池、缓存代理、服务网关
  • 系统编程:智能指针、系统调用代理
  • 分布式系统:服务网格、API网关、负载均衡器

掌握代理模式,意味着你不仅学会了一种设计模式,更学会了一种思考软件设计的方式——如何通过添加适当的间接层来创造更灵活、更高效、更安全的系统。

无论你是正在处理性能优化的前端工程师,还是构建分布式系统的后端架构师,代理模式都将是你工具箱中一件强大的武器。它优雅而强大,简单而深刻,真正体现了优秀软件设计的魅力。

http://www.dtcms.com/a/394686.html

相关文章:

  • 详解 Kubernetes 命令:kubectl exec -it nginx -- bash 及实战场景
  • Android挂机短信模板和多台手机同步短信模板
  • Solid Edge 转换为 IGS 全流程技术指南:含迪威模型网在线转换方案
  • 设计模式(C++)详解——代理模式 (Proxy Pattern)(1)
  • 聊聊AI agents MCP 开发
  • 【C++进阶】智能指针的使用及其原理
  • 极客天成让统一存储从云原生‘进化’到 AI 原生: 不是版本升级,而是基因重组
  • 【JavaScript 性能优化实战】第五篇:运行时性能优化进阶(懒加载 + 预加载 + 资源优先级)
  • Java基础(十二):抽象类与接口详解
  • 使用《微PE》软件,制作U盘启动盘(PE工具盘)
  • 《初阶 Linux 工具学习:Shell运行原理以及Linux权限讲解》
  • 树链剖分(模板 + 思路)
  • 医疗数据互操作性与联邦学习的python编程方向研究(上)
  • Windows最新摆烂更新,让用户没法看视频了
  • 可配置化App启动弹窗系统:实现后台动态管理与热更新引导-蜻蜓Q系统laravel+vue3-优雅草卓伊凡
  • Permute 媒体文件格式转换【音视频图像文件转换】(Mac电脑)
  • Netty:实现RPC服务(实战)
  • 408复习笔记—MIPS指令系统
  • 阿里万相2.1:蓝耘MaaS平台部署 vs 官网在线使用:万字实测对比与深度技术解析
  • 11月长春EI会议:ISRAI 2025 诚邀学者参与投稿
  • 【AI时代速通QT】第七节:Visual Studio+Qt 开发指南
  • 医疗问诊陪诊小程序:全方位守护就医体验的功能宝库
  • iOS 开发环境搭建完整指南 Xcode 安装配置、iOS 开发工具选择、ipa 打包与 App Store 上架实战经验
  • 【Node.js】Express 和 Koa 中间件的区别
  • 学习路之PHP--TP8+swoole
  • 【从零开始的大模型原理与实践教程】--第五章:动手搭建大模型LLaMA2
  • Vue.js 从入门到实践1:环境搭建、数据绑定与条件渲染
  • “潮涌之江,文兴浙里”文化推动高质量发展主题活动在西湖区调研
  • 【MongoDB】mongoDB数据迁移
  • 《C++多态入门:轻松理解虚函数与多态编程》