<摘要>

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

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

  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网关、负载均衡器

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

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

更多推荐