代理模式实战:代码里的 “全能中介”

目录

一、从租房找中介说起:为啥需要代理模式?

二、代理模式的核心原理:3 个角色 + 2 种实现

2.1 核心角色(3 个)

2.2 UML 类图(静态代理)

2.3 两种实现方式:静态代理 vs 动态代理

基础示例:租房中介(静态代理)

运行结果

核心亮点

动态代理(C++ 模板实现)

编译命令

运行结果

静态代理 vs 动态代理对比

三、3 个工业级 C++ 案例:从理论到项目实战

案例 1:权限控制代理 —— 后台接口的角色权限校验

场景描述

代码实现

运行结果

核心价值

案例 2:延迟加载代理 —— 大数据对象的懒加载优化

场景描述

代码实现

编译命令

运行结果

核心价值

案例 3:远程代理 ——RPC 调用中的本地代理

场景描述

代码实现

编译命令

运行结果

核心价值

四、代理模式的优缺点:什么时候用?什么时候不用?

4.1 优点

4.2 缺点

4.3 适用场景总结

4.4 不适用场景

五、避坑指南:实际开发中容易踩的 5 个坑

坑 1:代理类和真实主题接口不一致

坑 2:内存泄漏(未释放真实主题对象)

坑 3:过度代理导致调用链过长

坑 4:动态代理未处理非虚函数

坑 5:远程代理未处理网络异常

六、代理模式 vs 相似模式:面试高频考点

5.1 代理模式 vs 装饰器模式

5.2 代理模式 vs 适配器模式

5.3 代理模式 vs 外观模式

七、最佳实践:工业级项目中的 5 个建议

1. 抽象接口要稳定

2. 遵循单一职责原则

3. 合理选择静态 / 动态代理

4. 避免代理嵌套过深

5. 命名规范清晰

八、总结


class 卑微码农:
    def __init__(self):
        self.技能 = ['能读懂十年前祖传代码', '擅长用Ctrl+C/V搭建世界', '信奉"能跑就别动"的玄学']
        self.发量 = 100  # 初始发量
        self.咖啡因耐受度 = '极限'
        
    def 修Bug(self, bug):
        try:
            # 试图用玄学解决问题
            if bug.严重程度 == '离谱':
                print("这一定是环境问题!")
            else:
                print("让我看看是谁又没写注释...哦,是我自己。")
        except Exception as e:
            # 如果try块都救不了,那就...
            print("重启一下试试?")
            self.发量 -= 1  # 每解决一个bug,头发-1
 
 
# 实例化一个我
我 = 卑微码农()

一、从租房找中介说起:为啥需要代理模式?

你要换租房,没直接找房东,而是通过中介筛选房源、谈价格、签合同 —— 中介没改变 “租房” 这个核心需求,却帮你过滤了不靠谱的房东、简化了流程,还能在签约前核实房屋信息,这就是生活中典型的 “代理”。

再比如公司前台代收快递,不会让所有访客直接进办公区;游戏代练帮玩家升级,玩家不用自己肝任务;网购时平台作为卖家和买家的代理,保障交易安全 —— 这些场景的核心都是 “代为处理事务、控制访问权限、隐藏底层细节”。

软件开发中,我们每天都会遇到需要 “代理” 的场景:

  • 后台接口需要权限校验:普通用户不能访问管理员接口,得先通过 “权限代理” 核实身份;
  • 大数据对象加载耗时:比如加载 10G 的日志文件,不想启动时就加载,需要 “延迟加载代理” 按需加载;
  • 远程服务调用:调用另一台服务器的接口,不想关心网络通信细节,需要 “远程代理” 屏蔽网络差异;
  • 操作日志记录:需要记录接口的调用者、调用时间、参数,不想在业务代码里写日志,需要 “日志代理” 统一处理。

如果没有代理模式,这些功能只能硬编码到业务逻辑中:每个接口都写一遍权限校验、每个大数据对象都写懒加载逻辑、每个远程调用都处理网络异常 —— 代码会变得臃肿、耦合度极高,后续修改时要改所有相关代码,风险很大。

而代理模式,就是代码世界的 “全能中介”:它在不改变原始类(真实主题)的前提下,通过一个代理类(Proxy)控制对原始类的访问,还能在访问前后添加额外功能(如权限校验、日志、缓存),实现 “功能增强” 和 “解耦”。用官方定义说,代理模式(Proxy Pattern)是结构型设计模式的一种,核心是 “控制访问、代为处理”,让代理类和原始类实现同一接口,客户端通过代理类间接访问原始类。

这篇文章就从 C++ 实战出发,用 3 个工业级案例带你吃透代理模式,不管是面试还是项目开发,都能直接落地。

二、代理模式的核心原理:3 个角色 + 2 种实现

代理模式的核心是 “代理类和原始类实现同一接口”,客户端无需区分代理和原始类,只需调用接口方法,剩下的由代理类处理 —— 比如租房时,你只需要和中介谈(调用接口),中介会去对接房东(原始类)。

2.1 核心角色(3 个)

  • 抽象主题(Subject):定义核心功能的接口,比如 “租房接口”“数据查询接口”,是代理类和原始类的共同父类;
  • 真实主题(RealSubject):接口的实际实现者,是被代理的对象,比如 “房东”“数据库查询类”,负责核心业务逻辑;
  • 代理(Proxy):持有真实主题的引用,实现抽象主题接口,负责控制对真实主题的访问,可在访问前后添加额外功能。

2.2 UML 类图(静态代理)

2.3 两种实现方式:静态代理 vs 动态代理

代理模式在 C++ 中有两种常见实现:静态代理(编译时确定代理关系)和动态代理(运行时动态生成代理)。静态代理简单直观,适合场景固定的情况;动态代理灵活性高,适合需要批量代理多个类的场景。

基础示例:租房中介(静态代理)

用租房场景对应 3 个角色,写一个最小化可运行示例,直观理解静态代理的工作流程:

  • 抽象主题(Subject):RentHouse接口,提供 “租房” 方法;
  • 真实主题(RealSubject):Landlord(房东),实现 “租房” 核心逻辑;
  • 代理(Proxy):HouseAgent(中介),控制访问,添加筛选房源、核实身份、签合同等额外功能。
#include <iostream>
#include <string>
using namespace std;

// 1. 抽象主题(Subject):租房接口
class RentHouse {
public:
    virtual ~RentHouse() = default;
    virtual void rent(const string& tenant) = 0; // 核心方法:租房
};

// 2. 真实主题(RealSubject):房东(真正拥有房源的人)
class Landlord : public RentHouse {
private:
    string m_houseInfo; // 房源信息
public:
    Landlord(const string& houseInfo) : m_houseInfo(houseInfo) {}
    
    void rent(const string& tenant) override {
        // 核心业务逻辑:房东出租房屋
        cout << "房东直接出租:" << m_houseInfo << ",租客:" << tenant << endl;
    }
};

// 3. 代理(Proxy):租房中介(代理房东处理租房事务)
class HouseAgent : public RentHouse {
private:
    Landlord* m_landlord; // 持有真实主题的引用
    string m_feeRate;     // 中介服务费比例
    
public:
    // 构造函数注入房东对象和服务费比例
    HouseAgent(Landlord* landlord, const string& feeRate) 
        : m_landlord(landlord), m_feeRate(feeRate) {}
    
    ~HouseAgent() override {
        delete m_landlord; // 释放真实主题,避免内存泄漏
    }
    
    void rent(const string& tenant) override {
        // 代理的额外功能1:筛选租客(核实身份)
        if (!verifyTenant(tenant)) {
            cout << "中介筛选:租客" << tenant << "身份不符,拒绝租房" << endl;
            return;
        }
        
        // 代理的额外功能2:介绍房源、谈价格
        introduceHouse();
        
        // 调用真实主题的核心方法(房东出租)
        m_landlord->rent(tenant);
        
        // 代理的额外功能3:收取服务费、签合同
        collectFee(tenant);
        signContract(tenant);
    }
    
private:
    // 辅助方法:核实租客身份(模拟)
    bool verifyTenant(const string& tenant) {
        // 假设只有"张三"、"李四"是符合条件的租客
        return tenant == "张三" || tenant == "李四";
    }
    
    // 辅助方法:介绍房源
    void introduceHouse() {
        cout << "中介介绍:房源位于市中心,两室一厅,家电齐全,月租3000元" << endl;
    }
    
    // 辅助方法:收取服务费
    void collectFee(const string& tenant) {
        cout << "中介收取服务费:月租的" << m_feeRate << ",即300元" << endl;
    }
    
    // 辅助方法:签订合同
    void signContract(const string& tenant) {
        cout << "中介协助签订租房合同:租客" << tenant << ",租期1年" << endl;
    }
};

// 客户端:租客租房(只和中介交互,不直接找房东)
int main() {
    cout << "=== 静态代理:租房中介示例 ===" << endl;
    
    // 1. 房东有一套房源
    Landlord* landlord = new Landlord("市中心两室一厅");
    
    // 2. 中介代理房东的房源,服务费10%
    RentHouse* agent = new HouseAgent(landlord, "10%");
    
    // 3. 租客张三租房(符合条件)
    cout << "\n--- 租客张三租房 ---" << endl;
    agent->rent("张三");
    
    // 4. 租客王五租房(不符合条件)
    cout << "\n--- 租客王五租房 ---" << endl;
    agent->rent("王五");
    
    delete agent;
    return 0;
}
运行结果
=== 静态代理:租房中介示例 ===

--- 租客张三租房 ---
中介介绍:房源位于市中心,两室一厅,家电齐全,月租3000元
房东直接出租:市中心两室一厅,租客:张三
中介收取服务费:月租的10%,即300元
中介协助签订租房合同:租客张三,租期1年

--- 租客王五租房 ---
中介筛选:租客王五身份不符,拒绝租房
核心亮点
  • 客户端只和代理(中介)交互,无需知道真实主题(房东)的存在,隐藏了底层细节;
  • 代理在不修改房东代码的前提下,添加了筛选租客、介绍房源、收服务费等功能;
  • 接口一致:不管是直接找房东(如果允许)还是找中介,都调用rent()方法,客户端无感知。
动态代理(C++ 模板实现)

静态代理的缺点是 “一个真实主题对应一个代理类”,如果需要代理 10 个类,就要写 10 个代理类,代码冗余。动态代理可以在运行时动态生成代理,批量代理多个类,灵活性更高。

C++ 没有 Java 的Proxy类,但可以用模板 + 继承实现简单的动态代理,核心是用模板兼容不同的真实主题,用统一的代理类处理额外功能(如日志、计时)。

示例场景:给多个业务类(用户服务、订单服务)添加 “接口调用计时” 功能,用动态代理批量实现,无需为每个服务写单独代理。

#include <iostream>
#include <string>
#include <chrono>
using namespace std;
using namespace chrono;

// 1. 业务接口1:用户服务
class UserService {
public:
    virtual ~UserService() = default;
    virtual void getUserInfo(int userId) = 0; // 查询用户信息
    virtual void updateUserInfo(int userId, const string& name) = 0; // 更新用户信息
};

// 2. 真实主题1:用户服务实现
class UserServiceImpl : public UserService {
public:
    void getUserInfo(int userId) override {
        cout << "查询用户ID=" << userId << "的信息:姓名张三,年龄25" << endl;
        // 模拟业务耗时
        this_thread::sleep_for(milliseconds(100));
    }
    
    void updateUserInfo(int userId, const string& name) override {
        cout << "更新用户ID=" << userId << "的姓名为:" << name << endl;
        this_thread::sleep_for(milliseconds(150));
    }
};

// 3. 业务接口2:订单服务
class OrderService {
public:
    virtual ~OrderService() = default;
    virtual void createOrder(int userId, double amount) = 0; // 创建订单
};

// 4. 真实主题2:订单服务实现
class OrderServiceImpl : public OrderService {
public:
    void createOrder(int userId, double amount) override {
        cout << "用户ID=" << userId << "创建订单,金额:" << amount << "元" << endl;
        this_thread::sleep_for(milliseconds(200));
    }
};

// 5. 动态代理类(模板实现,兼容任意拥有虚函数的接口)
template <typename T>
class DynamicProxy : public T {
private:
    T* m_realSubject; // 真实主题对象
    
public:
    explicit DynamicProxy(T* realSubject) : m_realSubject(realSubject) {}
    
    ~DynamicProxy() override {
        delete m_realSubject;
    }
    
    // 重写用户服务的getUserInfo方法(添加计时功能)
    void getUserInfo(int userId) override {
        if (m_realSubject) {
            auto start = high_resolution_clock::now();
            m_realSubject->getUserInfo(userId); // 调用真实主题方法
            auto end = high_resolution_clock::now();
            auto duration = duration_cast<milliseconds>(end - start).count();
            cout << "【计时代理】getUserInfo调用耗时:" << duration << "ms" << endl;
        }
    }
    
    // 重写用户服务的updateUserInfo方法
    void updateUserInfo(int userId, const string& name) override {
        if (m_realSubject) {
            auto start = high_resolution_clock::now();
            m_realSubject->updateUserInfo(userId, name);
            auto end = high_resolution_clock::now();
            auto duration = duration_cast<milliseconds>(end - start).count();
            cout << "【计时代理】updateUserInfo调用耗时:" << duration << "ms" << endl;
        }
    }
    
    // 重写订单服务的createOrder方法
    void createOrder(int userId, double amount) override {
        if (m_realSubject) {
            auto start = high_resolution_clock::now();
            m_realSubject->createOrder(userId, amount);
            auto end = high_resolution_clock::now();
            auto duration = duration_cast<milliseconds>(end - start).count();
            cout << "【计时代理】createOrder调用耗时:" << duration << "ms" << endl;
        }
    }
};

// 客户端:调用业务接口(通过动态代理添加计时功能)
int main() {
    cout << "=== 动态代理:接口调用计时示例 ===" << endl;
    
    // 1. 代理用户服务
    cout << "\n--- 代理用户服务 ---" << endl;
    UserService* userProxy = new DynamicProxy<UserService>(new UserServiceImpl());
    userProxy->getUserInfo(1);
    userProxy->updateUserInfo(1, "李四");
    delete userProxy;
    
    // 2. 代理订单服务
    cout << "\n--- 代理订单服务 ---" << endl;
    OrderService* orderProxy = new DynamicProxy<OrderService>(new OrderServiceImpl());
    orderProxy->createOrder(1, 99.9);
    delete orderProxy;
    
    return 0;
}
编译命令
g++ -std=c++11 proxy_dynamic.cpp -o proxy_dynamic -pthread
运行结果
=== 动态代理:接口调用计时示例 ===

--- 代理用户服务 ---
查询用户ID=1的信息:姓名张三,年龄25
【计时代理】getUserInfo调用耗时:102ms
更新用户ID=1的姓名为:李四
【计时代理】updateUserInfo调用耗时:151ms

--- 代理订单服务 ---
用户ID=1创建订单,金额:99.9元
【计时代理】createOrder调用耗时:203ms
静态代理 vs 动态代理对比
对比维度静态代理动态代理(C++ 模板实现)
实现方式手动编写代理类,继承抽象主题模板 + 继承,运行时动态绑定真实主题
灵活性低:一个真实主题对应一个代理类高:一个代理类兼容多个真实主题
代码冗余度高:多个代理类重复写额外功能低:额外功能集中在一个代理类
可读性高:代码直观,调试方便中:需要理解模板机制,调试稍复杂
适用场景场景固定、代理逻辑复杂批量代理多个类、额外功能简单统一

实际开发建议:如果代理逻辑简单(如日志、计时),需要批量代理多个类,用动态代理;如果代理逻辑复杂(如权限校验、远程调用),场景固定,用静态代理,可读性和维护性更好。

三、3 个工业级 C++ 案例:从理论到项目实战

基础示例只是入门,真正的价值在于解决实际项目问题。下面 3 个案例覆盖 “权限控制”“延迟加载”“远程代理” 三大高频场景,代码符合工业级规范,可直接复制到项目中使用。

案例 1:权限控制代理 —— 后台接口的角色权限校验

场景描述

后台管理系统有多个接口(用户管理、订单管理、系统配置),不同角色(管理员、普通用户、游客)的访问权限不同:

  • 管理员:可访问所有接口;
  • 普通用户:可访问用户管理、订单管理接口,不可访问系统配置;
  • 游客:只能访问公开接口(如查询商品),不可访问任何管理接口。

要求:在不修改接口实现代码的前提下,统一拦截接口调用,进行权限校验,拒绝无权限访问。

代码实现
#include <iostream>
#include <string>
#include <unordered_set>
using namespace std;

// 角色枚举
enum class Role {
    GUEST,    // 游客
    USER,     // 普通用户
    ADMIN     // 管理员
};

// 1. 抽象主题:后台系统接口基类
class SystemInterface {
public:
    virtual ~SystemInterface() = default;
    virtual string getName() const = 0; // 获取接口名称
    virtual void execute(const string& userName, Role role) = 0; // 执行接口逻辑
};

// 2. 真实主题1:用户管理接口
class UserManager : public SystemInterface {
public:
    string getName() const override {
        return "用户管理接口";
    }
    
    void execute(const string& userName, Role role) override {
        // 核心业务逻辑:用户管理(查询、修改用户信息)
        cout << "[" << userName << "] 执行" << getName() << ":查询所有用户列表" << endl;
    }
};

// 3. 真实主题2:订单管理接口
class OrderManager : public SystemInterface {
public:
    string getName() const override {
        return "订单管理接口";
    }
    
    void execute(const string& userName, Role role) override {
        cout << "[" << userName << "] 执行" << getName() << ":查询所有订单数据" << endl;
    }
};

// 4. 真实主题3:系统配置接口
class SystemConfig : public SystemInterface {
public:
    string getName() const override {
        return "系统配置接口";
    }
    
    void execute(const string& userName, Role role) override {
        cout << "[" << userName << "] 执行" << getName() << ":修改系统端口号为8080" << endl;
    }
};

// 5. 代理:权限控制代理(统一校验所有接口的访问权限)
class PermissionProxy : public SystemInterface {
private:
    SystemInterface* m_realInterface; // 被代理的接口
    // 权限配置:key=接口名称,value=允许访问的角色集合
    unordered_map<string, unordered_set<Role>> m_permissionConfig;
    
public:
    explicit PermissionProxy(SystemInterface* realInterface) : m_realInterface(realInterface) {
        // 初始化权限配置(可从配置文件读取,这里简化硬编码)
        initPermissionConfig();
    }
    
    ~PermissionProxy() override {
        delete m_realInterface;
    }
    
    string getName() const override {
        return m_realInterface->getName();
    }
    
    void execute(const string& userName, Role role) override {
        // 代理的核心功能:权限校验
        string interfaceName = getName();
        
        // 1. 检查接口是否存在权限配置
        auto it = m_permissionConfig.find(interfaceName);
        if (it == m_permissionConfig.end()) {
            cout << "权限校验失败:" << interfaceName << "未配置访问权限" << endl;
            return;
        }
        
        // 2. 检查当前角色是否有权限
        const auto& allowedRoles = it->second;
        if (allowedRoles.find(role) == allowedRoles.end()) {
            cout << "权限校验失败:用户[" << userName << "](角色:" 
                 << roleToString(role) << ")无" << interfaceName << "访问权限" << endl;
            return;
        }
        
        // 3. 权限通过,执行真实接口逻辑
        cout << "权限校验通过:用户[" << userName << "](角色:" 
             << roleToString(role) << ")允许访问" << interfaceName << endl;
        m_realInterface->execute(userName, role);
    }
    
private:
    // 初始化权限配置
    void initPermissionConfig() {
        // 用户管理接口:允许管理员、普通用户访问
        m_permissionConfig["用户管理接口"].insert(Role::ADMIN);
        m_permissionConfig["用户管理接口"].insert(Role::USER);
        
        // 订单管理接口:允许管理员、普通用户访问
        m_permissionConfig["订单管理接口"].insert(Role::ADMIN);
        m_permissionConfig["订单管理接口"].insert(Role::USER);
        
        // 系统配置接口:仅允许管理员访问
        m_permissionConfig["系统配置接口"].insert(Role::ADMIN);
    }
    
    // 角色转字符串(辅助打印)
    string roleToString(Role role) const {
        switch (role) {
            case Role::GUEST: return "游客";
            case Role::USER: return "普通用户";
            case Role::ADMIN: return "管理员";
            default: return "未知角色";
        }
    }
};

// 客户端:模拟不同角色用户访问接口
int main() {
    cout << "=== 权限控制代理:后台接口权限校验 ===" << endl;
    
    // 1. 管理员访问所有接口
    cout << "\n--- 管理员(张三)访问接口 ---" << endl;
    SystemInterface* adminUserManager = new PermissionProxy(new UserManager());
    adminUserManager->execute("张三", Role::ADMIN);
    
    SystemInterface* adminOrderManager = new PermissionProxy(new OrderManager());
    adminOrderManager->execute("张三", Role::ADMIN);
    
    SystemInterface* adminSystemConfig = new PermissionProxy(new SystemConfig());
    adminSystemConfig->execute("张三", Role::ADMIN);
    
    delete adminUserManager;
    delete adminOrderManager;
    delete adminSystemConfig;
    
    // 2. 普通用户访问接口
    cout << "\n--- 普通用户(李四)访问接口 ---" << endl;
    SystemInterface* userUserManager = new PermissionProxy(new UserManager());
    userUserManager->execute("李四", Role::USER);
    
    SystemInterface* userSystemConfig = new PermissionProxy(new SystemConfig());
    userSystemConfig->execute("李四", Role::USER); // 无权限
    
    delete userUserManager;
    delete userSystemConfig;
    
    // 3. 游客访问接口
    cout << "\n--- 游客(王五)访问接口 ---" << endl;
    SystemInterface* guestOrderManager = new PermissionProxy(new OrderManager());
    guestOrderManager->execute("王五", Role::GUEST); // 无权限
    
    delete guestOrderManager;
    
    return 0;
}
运行结果
=== 权限控制代理:后台接口权限校验 ===

--- 管理员(张三)访问接口 ---
权限校验通过:用户[张三](角色:管理员)允许访问用户管理接口
[张三] 执行用户管理接口:查询所有用户列表
权限校验通过:用户[张三](角色:管理员)允许访问订单管理接口
[张三] 执行订单管理接口:查询所有订单数据
权限校验通过:用户[张三](角色:管理员)允许访问系统配置接口
[张三] 执行系统配置接口:修改系统端口号为8080

--- 普通用户(李四)访问接口 ---
权限校验通过:用户[李四](角色:普通用户)允许访问用户管理接口
[李四] 执行用户管理接口:查询所有用户列表
权限校验失败:用户[李四](角色:普通用户)无系统配置接口访问权限

--- 游客(王五)访问接口 ---
权限校验失败:用户[王五](角色:游客)无订单管理接口访问权限
核心价值
  • 权限校验逻辑集中在代理类,无需在每个接口实现中写重复代码,符合 “单一职责原则”;
  • 不修改原有接口实现,避免引入线上风险,符合 “开闭原则”;
  • 权限配置可灵活修改(如从数据库或配置文件读取),无需改动代码;
  • 客户端调用方式不变,只需传递用户角色,透明化权限校验过程。

案例 2:延迟加载代理 —— 大数据对象的懒加载优化

场景描述

项目中需要加载一个 “大数据报表” 对象,该对象包含 10 万条用户数据,加载过程需要读取数据库、计算统计信息,耗时 5 秒。但很多场景下,用户可能只是创建报表对象,并不一定会调用show()方法查看报表,如果启动时就加载,会严重影响系统启动速度。

要求:实现 “延迟加载”—— 只有当用户调用show()方法时,才加载大数据,否则不加载,提升系统启动性能。

代码实现
#include <iostream>
#include <string>
#include <vector>
#include <thread>
using namespace std;

// 1. 抽象主题:报表接口
class Report {
public:
    virtual ~Report() = default;
    virtual void show() = 0; // 展示报表
    virtual string getTitle() const = 0; // 获取报表标题
};

// 2. 真实主题:大数据报表(加载耗时)
class BigDataReport : public Report {
private:
    string m_title;
    vector<string> m_reportData; // 存储10万条报表数据
    
public:
    explicit BigDataReport(const string& title) : m_title(title) {
        // 注意:这里不加载数据,延迟到show()方法调用时加载
        cout << "大数据报表对象创建成功(未加载数据)" << endl;
    }
    
    string getTitle() const override {
        return m_title;
    }
    
    void show() override {
        // 核心逻辑:加载大数据(仅在第一次调用show()时执行)
        loadBigData();
        
        // 展示报表(简化:只打印前5条数据)
        cout << "\n=== " << getTitle() << " ===" << endl;
        for (int i = 0; i < min(5, (int)m_reportData.size()); ++i) {
            cout << "数据" << i + 1 << ":" << m_reportData[i] << endl;
        }
        cout << "报表共" << m_reportData.size() << "条数据,已展示前5条" << endl;
    }
    
private:
    // 加载大数据(模拟耗时操作)
    void loadBigData() {
        if (!m_reportData.empty()) {
            return; // 已加载过,直接返回
        }
        
        cout << "开始加载大数据报表(预计耗时5秒)..." << endl;
        // 模拟数据库查询和数据计算(耗时5秒)
        this_thread::sleep_for(chrono::seconds(5));
        
        // 模拟生成10万条数据
        for (int i = 0; i < 100000; ++i) {
            m_reportData.push_back("用户ID=" + to_string(i) + ",消费金额=" + to_string(rand() % 10000 + 100) + "元");
        }
        
        cout << "大数据报表加载完成!" << endl;
    }
};

// 3. 代理:延迟加载代理(控制数据加载时机)
class LazyLoadProxy : public Report {
private:
    Report* m_realReport; // 真实报表对象(延迟初始化)
    string m_title;
    bool m_isLoaded; // 是否已加载数据
    
public:
    explicit LazyLoadProxy(const string& title) : m_title(title), m_realReport(nullptr), m_isLoaded(false) {}
    
    ~LazyLoadProxy() override {
        delete m_realReport;
    }
    
    string getTitle() const override {
        return m_title;
    }
    
    void show() override {
        // 代理的核心功能:延迟初始化真实对象+延迟加载数据
        if (!m_realReport) {
            m_realReport = new BigDataReport(m_title);
        }
        
        // 调用真实报表的show()方法(内部会加载数据)
        m_realReport->show();
        m_isLoaded = true;
    }
    
    // 额外功能:判断是否已加载数据
    bool isLoaded() const {
        return m_isLoaded;
    }
};

// 客户端:模拟系统启动和用户操作
int main() {
    cout << "=== 延迟加载代理:大数据报表优化 ===" << endl;
    
    // 1. 系统启动:创建报表代理对象(不加载数据,快速启动)
    cout << "系统启动中,创建报表对象..." << endl;
    LazyLoadProxy* reportProxy = new LazyLoadProxy("2025年用户消费统计报表");
    cout << "系统启动完成!报表对象状态:" << (reportProxy->isLoaded() ? "已加载数据" : "未加载数据") << endl;
    
    // 2. 模拟用户未查看报表(3秒后执行其他操作)
    cout << "\n3秒后执行其他业务逻辑(用户未查看报表)..." << endl;
    this_thread::sleep_for(chrono::seconds(3));
    cout << "其他业务逻辑执行完成,报表对象状态:" << (reportProxy->isLoaded() ? "已加载数据" : "未加载数据") << endl;
    
    // 3. 模拟用户查看报表(触发数据加载)
    cout << "\n用户请求查看报表..." << endl;
    reportProxy->show();
    
    // 4. 再次查看报表(已加载数据,无需重复加载)
    cout << "\n用户再次请求查看报表..." << endl;
    reportProxy->show();
    
    delete reportProxy;
    return 0;
}
编译命令
g++ -std=c++11 proxy_lazy.cpp -o proxy_lazy -pthread
运行结果
=== 延迟加载代理:大数据报表优化 ===
系统启动中,创建报表对象...
系统启动完成!报表对象状态:未加载数据

3秒后执行其他业务逻辑(用户未查看报表)...
其他业务逻辑执行完成,报表对象状态:未加载数据

用户请求查看报表...
大数据报表对象创建成功(未加载数据)
开始加载大数据报表(预计耗时5秒)...
大数据报表加载完成!

=== 2025年用户消费统计报表 ===
数据1:用户ID=0,消费金额=5678元
数据2:用户ID=1,消费金额=1234元
数据3:用户ID=2,消费金额=9876元
数据4:用户ID=3,消费金额=4321元
数据5:用户ID=4,消费金额=6789元
报表共100000条数据,已展示前5条

用户再次请求查看报表...
=== 2025年用户消费统计报表 ===
数据1:用户ID=0,消费金额=5678元
数据2:用户ID=1,消费金额=1234元
数据3:用户ID=2,消费金额=9876元
数据4:用户ID=3,消费金额=4321元
数据5:用户ID=4,消费金额=6789元
报表共100000条数据,已展示前5条
核心价值
  • 提升系统启动速度:避免启动时加载不必要的大数据,启动时间从 5 秒缩短到毫秒级;
  • 按需加载:只有用户实际使用时才加载数据,节省内存和 CPU 资源;
  • 透明化:客户端调用方式不变,无需关心数据是否加载,代理类自动处理;
  • 缓存机制:数据加载一次后缓存,后续调用无需重复加载,提升性能。

案例 3:远程代理 ——RPC 调用中的本地代理

场景描述

分布式系统中,服务 A(客户端)需要调用服务 B(服务器端)的 “用户信息查询” 接口。服务 A 和服务 B 部署在不同服务器,通信需要通过网络(TCP/UDP),涉及数据序列化 / 反序列化、网络连接、异常处理等复杂细节。

要求:服务 A 无需关心网络通信细节,通过一个 “本地代理” 调用接口,代理类隐藏网络交互、序列化等底层逻辑,让远程调用像本地调用一样简单。

代码实现
#include <iostream>
#include <string>
#include <thread>
#include <cstring>
using namespace std;

// 模拟序列化/反序列化工具(实际项目中用Protobuf、Thrift)
class Serializer {
public:
    // 序列化:将用户ID转换为网络传输的字节流
    static string serialize(int userId) {
        return to_string(userId); // 简化:直接转为字符串
    }
    
    // 反序列化:将字节流转换为用户信息字符串
    static string deserialize(const string& data) {
        // 模拟服务器返回的用户信息
        int userId = stoi(data);
        return "{\"user_id\":" + to_string(userId) + ",\"name\":\"张三\",\"age\":25,\"address\":\"北京市\"}";
    }
};

// 模拟网络通信工具(实际项目中用Socket、Boost.Asio)
class NetworkClient {
public:
    // 发送请求并接收响应(模拟网络延迟)
    static string sendRequest(const string& serverIp, int port, const string& data) {
        cout << "网络请求:向服务器" << serverIp << ":" << port << "发送数据:" << data << endl;
        // 模拟网络延迟(500ms)
        this_thread::sleep_for(chrono::milliseconds(500));
        // 模拟服务器响应(实际是接收服务器返回的字节流)
        cout << "网络响应:接收服务器数据成功" << endl;
        return data; // 返回原始数据,实际应返回服务器响应
    }
};

// 1. 抽象主题:用户服务接口(本地和远程都实现该接口)
class UserService {
public:
    virtual ~UserService() = default;
    virtual string getUserInfo(int userId) = 0; // 查询用户信息
};

// 2. 真实主题:远程服务器的用户服务(部署在另一台服务器)
class RemoteUserService : public UserService {
private:
    string m_serverIp;
    int m_port;
    
public:
    RemoteUserService(const string& serverIp, int port) : m_serverIp(serverIp), m_port(port) {}
    
    string getUserInfo(int userId) override {
        // 服务器端逻辑:接收客户端请求,查询数据库,返回结果
        cout << "远程服务器:处理用户ID=" << userId << "的查询请求" << endl;
        // 模拟查询数据库
        return "{\"user_id\":" + to_string(userId) + ",\"name\":\"张三\",\"age\":25,\"address\":\"北京市\"}";
    }
};

// 3. 代理:本地代理(服务A的本地代理,隐藏网络和序列化细节)
class LocalProxy : public UserService {
private:
    string m_serverIp; // 远程服务器IP
    int m_port;        // 远程服务器端口
    
public:
    LocalProxy(const string& serverIp, int port) : m_serverIp(serverIp), m_port(port) {}
    
    string getUserInfo(int userId) override {
        // 代理的核心功能:隐藏远程调用的复杂细节
        try {
            // 1. 序列化请求参数(用户ID)
            string requestData = Serializer::serialize(userId);
            cout << "本地代理:参数序列化完成,数据:" << requestData << endl;
            
            // 2. 网络通信:发送请求到远程服务器
            string responseData = NetworkClient::sendRequest(m_serverIp, m_port, requestData);
            
            // 3. 反序列化响应数据(将服务器返回的字节流转为用户信息)
            string userInfo = Serializer::deserialize(responseData);
            cout << "本地代理:响应反序列化完成,用户信息:" << userInfo << endl;
            
            return userInfo;
        } catch (const exception& e) {
            cout << "本地代理:远程调用失败,原因:" << e.what() << endl;
            return "{\"code\":500,\"msg\":\"远程调用失败\"}";
        }
    }
};

// 模拟远程服务器(监听端口,处理客户端请求)
class RemoteServer {
private:
    string m_ip;
    int m_port;
    bool m_running;
    UserService* m_userService;
    
public:
    RemoteServer(const string& ip, int port) : m_ip(ip), m_port(port), m_running(false) {
        m_userService = new RemoteUserService(ip, port);
    }
    
    ~RemoteServer() {
        delete m_userService;
    }
    
    // 启动服务器(模拟)
    void start() {
        m_running = true;
        cout << "远程服务器启动:" << m_ip << ":" << m_port << ",监听客户端请求..." << endl;
        // 实际服务器会启动线程监听端口,这里简化不实现
    }
    
    // 停止服务器
    void stop() {
        m_running = false;
        cout << "远程服务器停止:" << m_ip << ":" << m_port << endl;
    }
};

// 客户端:服务A调用远程服务B的接口(通过本地代理)
int main() {
    cout << "=== 远程代理:RPC调用示例 ===" << endl;
    
    // 1. 启动远程服务器(模拟服务B启动)
    RemoteServer server("192.168.1.100", 8080);
    server.start();
    cout << endl;
    
    // 2. 服务A创建本地代理(连接远程服务器)
    UserService* userProxy = new LocalProxy("192.168.1.100", 8080);
    
    // 3. 调用接口(像本地调用一样简单,无需关心网络)
    cout << "客户端:调用getUserInfo查询用户ID=1的信息" << endl;
    string userInfo = userProxy->getUserInfo(1);
    cout << "客户端:最终获取的用户信息:" << userInfo << endl;
    
    // 4. 停止服务器和代理
    delete userProxy;
    server.stop();
    
    return 0;
}
编译命令
g++ -std=c++11 proxy_remote.cpp -o proxy_remote -pthread
运行结果
=== 远程代理:RPC调用示例 ===
远程服务器启动:192.168.1.100:8080,监听客户端请求...

客户端:调用getUserInfo查询用户ID=1的信息
本地代理:参数序列化完成,数据:1
网络请求:向服务器192.168.1.100:8080发送数据:1
网络响应:接收服务器数据成功
本地代理:响应反序列化完成,用户信息:{"user_id":1,"name":"张三","age":25,"address":"北京市"}
客户端:最终获取的用户信息:{"user_id":1,"name":"张三","age":25,"address":"北京市"}
远程服务器停止:192.168.1.100:8080
核心价值
  • 屏蔽复杂细节:客户端无需关心网络通信、序列化 / 反序列化、异常处理,降低开发难度;
  • 透明化远程调用:远程调用像本地调用一样简单,提升开发效率;
  • 解耦:客户端与远程服务通过代理隔离,后续修改网络协议或序列化方式,只需修改代理类;
  • 可扩展性:代理类可添加缓存、超时重试、负载均衡等功能,提升系统稳定性和性能。

四、代理模式的优缺点:什么时候用?什么时候不用?

4.1 优点

  1. 控制访问:可在访问真实主题前添加权限校验、日志记录、缓存等功能,实现对访问的精细化控制;
  2. 解耦:将非核心逻辑(如网络通信、权限校验)与核心业务逻辑分离,降低代码耦合度;
  3. 隐藏细节:隐藏真实主题的复杂实现(如远程服务的网络细节、大数据对象的加载逻辑),简化客户端使用;
  4. 增强功能:在不修改真实主题代码的前提下,通过代理添加额外功能,符合 “开闭原则”;
  5. 灵活性高:动态代理可批量代理多个类,静态代理可定制复杂逻辑,适应不同场景。

4.2 缺点

  1. 增加系统复杂度:引入代理类会增加代码量和理解成本,调试时需要跟踪代理和真实主题的调用链;
  2. 轻微性能损耗:代理类会额外增加一层调用,对于高频调用的核心接口,可能影响性能;
  3. 接口一致性要求高:代理类和真实主题必须实现同一接口,否则客户端无法透明调用;
  4. 动态代理学习成本高:C++ 的动态代理需要掌握模板、虚函数、继承等知识,上手难度比静态代理高。

4.3 适用场景总结

  • 权限控制:需要对接口访问进行身份校验、角色限制(如后台管理系统);
  • 延迟加载:大数据对象、耗时操作需要按需加载,提升系统启动速度和性能;
  • 远程调用:分布式系统中,远程服务调用需要隐藏网络通信细节(如 RPC、微服务);
  • 日志 / 监控:需要记录接口调用日志、统计调用耗时、上报监控数据(如接口监控系统);
  • 缓存优化:频繁调用的接口,通过代理缓存结果,减少重复计算或数据库查询;
  • 保护真实主题:需要隐藏真实主题的实现细节,或限制对真实主题的直接访问(如第三方 API 调用)。

4.4 不适用场景

  • 核心高频接口:对性能要求极高的核心接口(如每秒调用 10 万次的算法接口),代理的性能损耗会被放大;
  • 简单功能:功能简单、无需额外控制或增强的场景,引入代理会增加不必要的复杂度;
  • 接口频繁变化:如果抽象主题接口频繁修改,代理类和真实主题都需要同步修改,维护成本高。

五、避坑指南:实际开发中容易踩的 5 个坑

坑 1:代理类和真实主题接口不一致

问题:代理类忘记实现抽象主题的某个方法,或方法签名不一致(如参数类型、返回值),导致客户端调用时编译错误或运行时异常。解决方案:严格保证代理类和真实主题实现同一抽象接口,重写所有虚函数,可通过 IDE 的 “重写” 功能自动生成方法,避免手动编写出错。

坑 2:内存泄漏(未释放真实主题对象)

问题:代理类持有真实主题的指针,但析构函数未删除,导致内存泄漏。解决方案:在代理类的析构函数中,删除真实主题对象(delete m_realSubject);如果真实主题由客户端管理(不通过代理创建),需明确 ownership,避免重复删除。

坑 3:过度代理导致调用链过长

问题:多个代理嵌套(如权限代理→日志代理→缓存代理→真实主题),导致调用链过长,调试困难,性能损耗叠加。解决方案:合理拆分代理职责,核心代理(如权限、延迟加载)保留,非核心代理(如简单日志)可合并到业务逻辑或使用 AOP 框架;避免不必要的代理嵌套。

坑 4:动态代理未处理非虚函数

问题:抽象主题中存在非虚函数,动态代理无法重写,导致额外功能(如计时、日志)无法生效。解决方案:抽象主题的所有接口方法必须声明为虚函数,确保代理类能重写;非接口方法(辅助方法)尽量定义为 private 或 protected,避免客户端直接调用。

坑 5:远程代理未处理网络异常

问题:远程代理未捕获网络连接失败、超时、服务器宕机等异常,导致客户端崩溃。解决方案:在代理类的网络调用逻辑中,添加异常捕获(try-catch),处理超时重试、连接失败提示、降级返回等场景,保证系统稳定性。

六、代理模式 vs 相似模式:面试高频考点

很多开发者会把代理模式和装饰器模式、适配器模式、外观模式搞混,这是面试中的高频考点。用通俗的比喻和表格对比,让你一眼分清:

5.1 代理模式 vs 装饰器模式

对比维度代理模式装饰器模式
核心目的控制访问、隐藏细节(如权限、远程调用)增强功能、动态组合(如日志 + 加密)
关注点对真实主题的 “访问控制”对真实主题的 “功能增强”
客户端感知通常不知道真实主题的存在(如远程代理)知道真实主题,主动添加装饰(如奶茶加料)
组合方式通常是单层代理(少数嵌套)支持多层装饰,灵活组合
通俗比喻租房中介(控制访问、代为处理)奶茶加料(增强功能、动态组合)

5.2 代理模式 vs 适配器模式

对比维度代理模式适配器模式
核心目的控制访问、隐藏细节适配接口,解决不兼容问题
接口关系代理和真实主题实现同一接口适配器转换不同接口(目标接口≠适配者接口)
客户端感知无感知,调用方式不变无感知,但适配的是不同接口
适用场景权限、延迟加载、远程调用遗留系统改造、第三方库整合
通俗比喻中介(同一需求,代为处理)转换插头(接口不同,适配使用)

5.3 代理模式 vs 外观模式

对比维度代理模式外观模式
核心目的控制对单个对象的访问简化多个对象的复杂交互(统一入口)
处理对象数量代理一个真实主题封装多个子系统或对象
接口关系代理和真实主题实现同一接口外观类提供新接口,不实现子系统接口
适用场景单个对象的访问控制、细节隐藏复杂系统的简化调用(如家电遥控器)
通俗比喻个人中介(服务一个客户)前台接待(对接多个部门)

七、最佳实践:工业级项目中的 5 个建议

1. 抽象接口要稳定

抽象主题(Subject)的接口一旦定义,尽量不要频繁修改。如果必须修改,需同步更新代理类和真实主题,避免接口不一致。

2. 遵循单一职责原则

每个代理类只负责一个核心功能(如权限代理只做权限校验,日志代理只做日志记录),避免一个代理类同时处理权限、日志、缓存等多个功能,导致代码臃肿、难以维护。

3. 合理选择静态 / 动态代理

  • 静态代理:适合代理逻辑复杂、场景固定的场景(如远程代理、权限代理),可读性和维护性更好;
  • 动态代理:适合批量代理多个类、额外功能简单统一的场景(如日志、计时),减少代码冗余。

4. 避免代理嵌套过深

代理嵌套最多不超过 3 层(如权限代理→缓存代理→真实主题),超过 3 层会导致调用链过长、调试困难,可通过合并代理或使用 AOP 框架优化。

5. 命名规范清晰

代理类命名建议包含 “Proxy”,并体现核心功能,如PermissionProxy(权限代理)、LazyLoadProxy(延迟加载代理)、RemoteProxy(远程代理),便于团队成员识别。

八、总结

代理模式的核心是 “控制访问、隐藏细节、解耦增强”,它就像代码世界的 “全能中介”,在不改变原始类的前提下,代为处理复杂事务、控制访问权限、增强核心功能,让客户端只需关注自己的业务逻辑,无需关心底层细节。

通过本文的 3 个工业级案例,相信你已经掌握了代理模式的 C++ 实现和应用场景:权限控制代理解决 “谁能访问” 的问题,延迟加载代理解决 “何时加载” 的问题,远程代理解决 “如何跨服务器调用” 的问题。

记住:代理模式不是 “越多越好”,而是 “按需使用”。在实际项目中,当你遇到需要控制访问、隐藏细节、解耦非核心逻辑的场景时,不妨试试代理模式 —— 它会让你的代码更简洁、更易维护、更具扩展性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值