目录

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 优点
- 控制访问:可在访问真实主题前添加权限校验、日志记录、缓存等功能,实现对访问的精细化控制;
- 解耦:将非核心逻辑(如网络通信、权限校验)与核心业务逻辑分离,降低代码耦合度;
- 隐藏细节:隐藏真实主题的复杂实现(如远程服务的网络细节、大数据对象的加载逻辑),简化客户端使用;
- 增强功能:在不修改真实主题代码的前提下,通过代理添加额外功能,符合 “开闭原则”;
- 灵活性高:动态代理可批量代理多个类,静态代理可定制复杂逻辑,适应不同场景。
4.2 缺点
- 增加系统复杂度:引入代理类会增加代码量和理解成本,调试时需要跟踪代理和真实主题的调用链;
- 轻微性能损耗:代理类会额外增加一层调用,对于高频调用的核心接口,可能影响性能;
- 接口一致性要求高:代理类和真实主题必须实现同一接口,否则客户端无法透明调用;
- 动态代理学习成本高: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++ 实现和应用场景:权限控制代理解决 “谁能访问” 的问题,延迟加载代理解决 “何时加载” 的问题,远程代理解决 “如何跨服务器调用” 的问题。
记住:代理模式不是 “越多越好”,而是 “按需使用”。在实际项目中,当你遇到需要控制访问、隐藏细节、解耦非核心逻辑的场景时,不妨试试代理模式 —— 它会让你的代码更简洁、更易维护、更具扩展性。
238

被折叠的 条评论
为什么被折叠?



