第20章 设计模式实战:C++ 中代理模式与组合模式的深度应用

20.1 本章学习目标与重点
💡 掌握代理模式的核心设计思想、三种核心类型(静态代理、动态代理、远程代理)及 C++ 实现细节
💡 理解组合模式的树形结构设计逻辑、透明组合与安全组合的区别及适用场景
💡 能够结合实际业务场景(如权限控制、资源代理、树形结构管理)灵活运用两种模式
💡 解决模式应用中的关键问题(如代理模式的性能损耗、组合模式的节点遍历效率)
重点:代理模式的职责隔离逻辑、组合模式的统一节点操作技巧、两种模式的组合使用场景
20.2 设计模式进阶认知:结构型模式的扩展价值
在前一章装饰者模式与适配器模式的基础上,本章继续深入结构型设计模式的核心应用。结构型模式的核心价值不仅在于“组合对象”,更在于通过合理的结构设计,实现“职责分离”“层级管理”等高级目标。
20.2.1 为什么需要代理模式与组合模式?
实际开发中,我们常遇到以下复杂场景问题:
- 需为某个对象提供“中间层”(如权限校验、日志记录、资源缓存),但不想修改对象本身的代码(如第三方库对象、核心业务对象);
- 需访问远程对象(如分布式系统中的服务),但不想暴露远程调用的复杂细节(如网络连接、数据序列化);
- 需管理树形结构数据(如文件系统、组织架构、菜单层级),但希望对单个节点和整个树结构执行统一操作(如遍历、删除、统计);
- 树形结构中节点类型多样(如文件系统中的文件和文件夹),但希望客户端无需区分节点类型即可统一处理。
代理模式通过“中间代理”实现职责分离,组合模式通过“统一接口”实现树形结构的层级管理,两者分别解决“对象访问控制”和“树形结构操作”的核心问题。
20.2.2 结构型模式的进阶设计原则
结合本章两种模式的特性,需重点牢记以下进阶设计原则:
- 职责分离原则:将核心功能与辅助功能(如权限、日志、缓存)分离,通过代理类封装辅助功能;
- 统一接口原则:树形结构中所有节点(叶子节点、容器节点)实现统一接口,确保客户端操作一致性;
- 透明性原则:代理类与被代理类、叶子节点与容器节点对外暴露一致的接口,客户端无需感知差异;
- 最小知识原则:客户端只需与代理类、树形结构的根节点交互,无需了解内部实现细节。
20.3 代理模式:职责分离与对象访问控制的中间层方案
代理模式(Proxy Pattern)的核心思想是“为其他对象提供一种代理以控制对这个对象的访问”。代理类充当“中间层”,负责处理与被代理对象相关的辅助逻辑(如权限校验、日志记录、缓存、远程调用),而被代理对象专注于核心业务逻辑。
20.3.1 核心角色与设计逻辑
1. 核心角色
- 抽象主题(Subject):定义被代理对象和代理对象的共同接口,确保两者可替换;
- 真实主题(RealSubject):被代理的核心对象,实现抽象主题接口,包含核心业务逻辑;
- 代理(Proxy):实现抽象主题接口,持有真实主题的引用,在调用真实主题方法前后执行辅助逻辑,最终委托真实主题完成核心操作。
2. 设计逻辑
代理类与真实主题类继承自同一抽象主题接口,确保客户端可无缝替换使用;代理类通过组合方式持有真实主题的引用,而非继承,降低耦合度;客户端调用代理类方法时,代理类先执行辅助逻辑(如权限校验),再调用真实主题的核心方法,最后可执行后续处理(如日志记录)。
20.3.2 代理模式的三种核心类型与适用场景
| 代理类型 | 核心特点 | 适用场景 |
|---|---|---|
| 静态代理 | 代理类在编译期确定,与真实主题一一对应 | 辅助逻辑固定(如固定权限校验、简单日志)、场景简单 |
| 动态代理 | 代理类在运行时动态生成,可代理多个真实主题 | 辅助逻辑统一(如全局日志、通用缓存)、需代理多个类 |
| 远程代理 | 代理类负责远程通信细节,真实主题在远程服务器 | 分布式系统、跨进程调用、远程服务访问 |
20.3.3 实现方式 1:静态代理(基础应用)
以“用户权限控制”为例:系统中某些核心功能(如修改用户信息、删除数据)需要管理员权限才能执行,使用静态代理为核心功能添加权限校验逻辑,无需修改核心业务代码。
步骤 1:定义抽象主题(核心功能接口)
#include <iostream>
#include <string>
using namespace std;
// 抽象主题:用户管理接口(核心功能)
class UserManager {
public:
// 纯虚函数:修改用户信息
virtual bool ModifyUserInfo(const string& userId, const string& newInfo) = 0;
// 纯虚函数:删除用户
virtual bool DeleteUser(const string& userId) = 0;
virtual ~UserManager() {}
};
步骤 2:实现真实主题(核心业务逻辑)
// 真实主题:用户管理服务(核心业务实现)
class UserManagerService : public UserManager {
public:
bool ModifyUserInfo(const string& userId, const string& newInfo) override {
// 核心业务逻辑:模拟修改用户信息
cout << "[核心业务] 成功修改用户[" << userId << "]的信息为:" << newInfo << endl;
return true;
}
bool DeleteUser(const string& userId) override {
// 核心业务逻辑:模拟删除用户
cout << "[核心业务] 成功删除用户[" << userId << "]" << endl;
return true;
}
};
步骤 3:实现静态代理(添加权限校验)
// 静态代理:用户管理权限代理(添加权限校验辅助逻辑)
class UserManagerProxy : public UserManager {
private:
// 持有真实主题的引用(组合方式)
UserManager* realSubject;
// 权限校验所需的管理员账号
string adminAccount;
public:
// 构造函数:传入真实主题和管理员账号
UserManagerProxy(UserManager* realService, const string& admin)
: realSubject(realService), adminAccount(admin) {
if (realService == nullptr) {
throw invalid_argument("真实主题对象不能为空!");
}
}
~UserManagerProxy() {
// 代理类不负责销毁真实主题(由客户端管理)
}
// 权限校验辅助方法(代理类的核心辅助逻辑)
bool CheckPermission(const string& operatorAccount) const {
cout << "[权限校验] 正在校验操作者[" << operatorAccount << "]的权限..." << endl;
// 模拟权限校验:只有管理员账号才能执行操作
if (operatorAccount == adminAccount) {
cout << "[权限校验] 校验通过,操作者为管理员" << endl;
return true;
} else {
cout << "[权限校验] 校验失败,操作者无管理员权限" << endl;
return false;
}
}
// 代理修改用户信息:先校验权限,再调用真实主题方法
bool ModifyUserInfo(const string& userId, const string& newInfo) override {
// 1. 执行辅助逻辑:权限校验
string operatorAccount = "current_user"; // 模拟当前操作者账号
if (!CheckPermission(operatorAccount)) {
return false;
}
// 2. 委托真实主题执行核心业务
return realSubject->ModifyUserInfo(userId, newInfo);
}
// 代理删除用户:先校验权限,再调用真实主题方法
bool DeleteUser(const string& userId) override {
// 1. 执行辅助逻辑:权限校验
string operatorAccount = "current_user";
if (!CheckPermission(operatorAccount)) {
return false;
}
// 2. 委托真实主题执行核心业务
return realSubject->DeleteUser(userId);
}
};
步骤 4:客户端使用示例
int main() {
try {
// 1. 创建真实主题(核心业务对象)
UserManager* realService = new UserManagerService();
// 2. 创建代理对象(传入真实主题和管理员账号"admin")
UserManager* proxy = new UserManagerProxy(realService, "admin");
// 3. 客户端通过代理对象执行操作(无管理员权限,操作失败)
cout << "=== 无管理员权限操作 ===" << endl;
proxy->ModifyUserInfo("U1001", "姓名:张三,年龄:25");
proxy->DeleteUser("U1002");
// 4. 修改当前操作者账号为管理员(模拟有权限操作)
// 注:实际项目中可通过登录态获取操作者账号,此处简化为直接修改代理类逻辑
// 为演示效果,修改代理类的操作者账号(实际应通过接口传入)
cout << "\n=== 管理员权限操作(修改操作者账号为admin) ===" << endl;
// 重新创建代理对象,模拟操作者为管理员
UserManager* adminProxy = new UserManagerProxy(realService, "admin");
// 此处通过修改代理类的operatorAccount为"admin"来模拟,实际项目中应从外部传入
// 为简化代码,直接调用(实际需调整代理类接口,此处仅作演示)
adminProxy->ModifyUserInfo("U1001", "姓名:张三,年龄:25");
adminProxy->DeleteUser("U1002");
// 5. 释放资源
delete adminProxy;
delete proxy;
delete realService;
} catch (const exception& e) {
cout << "错误:" << e.what() << endl;
}
return 0;
}
运行结果
=== 无管理员权限操作 ===
[权限校验] 正在校验操作者[current_user]的权限...
[权限校验] 校验失败,操作者无管理员权限
[权限校验] 正在校验操作者[current_user]的权限...
[权限校验] 校验失败,操作者无管理员权限
=== 管理员权限操作(修改操作者账号为admin) ===
[权限校验] 正在校验操作者[current_user]的权限...
[权限校验] 校验失败,操作者无管理员权限
[权限校验] 正在校验操作者[current_user]的权限...
[权限校验] 校验失败,操作者无管理员权限
⚠️ 说明:上述示例中,由于 operatorAccount 硬编码为 current_user,即使创建 adminProxy 也无法通过校验。实际项目中,operatorAccount 应从客户端传入(如通过代理类的构造函数或方法参数),以下是修正后的代理类方法:
// 修正后的代理类方法:从参数接收操作者账号
bool ModifyUserInfo(const string& userId, const string& newInfo, const string& operatorAccount) {
if (!CheckPermission(operatorAccount)) {
return false;
}
return realSubject->ModifyUserInfo(userId, newInfo);
}
// 客户端调用
adminProxy->ModifyUserInfo("U1001", "姓名:张三,年龄:25", "admin"); // 校验通过
修正后的运行结果(关键片段):
[权限校验] 正在校验操作者[admin]的权限...
[权限校验] 校验通过,操作者为管理员
[核心业务] 成功修改用户[U1001]的信息为:姓名:张三,年龄:25
20.3.4 实现方式 2:动态代理(C++11 基于函数对象实现)
静态代理的缺点是“一个真实主题对应一个代理类”,若需为多个类添加统一的辅助逻辑(如全局日志记录),会导致代理类数量爆炸。动态代理可在运行时动态生成代理对象,实现对多个类的统一代理。
C++ 没有原生的动态代理机制(如 Java 的 Proxy 类),但可通过“函数对象+模板”模拟动态代理的核心功能。以下以“全局日志记录”为例,实现支持多个类的动态代理。
步骤 1:定义日志记录辅助类(统一辅助逻辑)
#include <iostream>
#include <string>
#include <functional>
#include <ctime>
using namespace std;
// 日志记录辅助类(统一的辅助逻辑)
class LogHelper {
public:
// 记录方法调用前的日志
static void LogBefore(const string& methodName) {
time_t now = time(nullptr);
cout << "\n[动态代理-日志] " << ctime(&now) << " 开始调用方法:" << methodName << endl;
}
// 记录方法调用后的日志
static void LogAfter(const string& methodName, bool result) {
cout << "[动态代理-日志] 方法" << methodName << "调用完成,结果:" << (result ? "成功" : "失败") << endl;
}
};
步骤 2:实现动态代理模板(支持任意类和方法)
// 动态代理模板:支持任意类的方法代理,添加统一日志逻辑
template <typename T>
class DynamicProxy {
private:
// 持有真实主题的指针
T* realSubject;
public:
DynamicProxy(T* realObj) : realSubject(realObj) {
if (realObj == nullptr) {
throw invalid_argument("真实主题对象不能为空!");
}
}
~DynamicProxy() {}
// 代理无参数、返回bool的方法
template <typename Func>
bool ProxyMethod(Func func, const string& methodName) {
// 1. 执行统一辅助逻辑:日志记录(调用前)
LogHelper::LogBefore(methodName);
// 2. 委托真实主题执行核心方法
bool result = (realSubject->*func)();
// 3. 执行统一辅助逻辑:日志记录(调用后)
LogHelper::LogAfter(methodName, result);
return result;
}
// 代理单参数、返回bool的方法(模板重载)
template <typename Func, typename Arg1>
bool ProxyMethod(Func func, const string& methodName, const Arg1& arg1) {
LogHelper::LogBefore(methodName);
bool result = (realSubject->*func)(arg1);
LogHelper::LogAfter(methodName, result);
return result;
}
// 代理双参数、返回bool的方法(模板重载)
template <typename Func, typename Arg1, typename Arg2>
bool ProxyMethod(Func func, const string& methodName, const Arg1& arg1, const Arg2& arg2) {
LogHelper::LogBefore(methodName);
bool result = (realSubject->*func)(arg1, arg2);
LogHelper::LogAfter(methodName, result);
return result;
}
};
步骤 3:定义多个真实主题(测试多类代理)
// 真实主题1:商品管理服务
class ProductManager {
public:
bool AddProduct(const string& productId) {
cout << "[商品管理] 成功添加商品:" << productId << endl;
return true;
}
bool UpdateProduct(const string& productId, const string& newDesc) {
cout << "[商品管理] 成功更新商品[" << productId << "]描述:" << newDesc << endl;
return true;
}
};
// 真实主题2:订单管理服务
class OrderManager {
public:
bool CreateOrder(const string& orderId) {
cout << "[订单管理] 成功创建订单:" << orderId << endl;
return true;
}
bool CancelOrder(const string& orderId) {
cout << "[订单管理] 成功取消订单:" << orderId << endl;
return true;
}
};
步骤 4:客户端使用示例(代理多个类)
int main() {
try {
// 1. 代理商品管理服务
ProductManager* productService = new ProductManager();
DynamicProxy<ProductManager> productProxy(productService);
cout << "=== 代理商品管理服务 ===" << endl;
productProxy.ProxyMethod(&ProductManager::AddProduct, "AddProduct", "P20240505001");
productProxy.ProxyMethod(&ProductManager::UpdateProduct, "UpdateProduct", "P20240505001", "高性能游戏本");
// 2. 代理订单管理服务
OrderManager* orderService = new OrderManager();
DynamicProxy<OrderManager> orderProxy(orderService);
cout << "\n=== 代理订单管理服务 ===" << endl;
orderProxy.ProxyMethod(&OrderManager::CreateOrder, "CreateOrder", "ORD20240505001");
orderProxy.ProxyMethod(&OrderManager::CancelOrder, "CancelOrder", "ORD20240505001");
// 3. 释放资源
delete orderService;
delete productService;
} catch (const exception& e) {
cout << "错误:" << e.what() << endl;
}
return 0;
}
运行结果
=== 代理商品管理服务 ===
[动态代理-日志] Mon May 5 14:00:00 2024
开始调用方法:AddProduct
[商品管理] 成功添加商品:P20240505001
[动态代理-日志] 方法AddProduct调用完成,结果:成功
[动态代理-日志] Mon May 5 14:00:00 2024
开始调用方法:UpdateProduct
[商品管理] 成功更新商品[P20240505001]描述:高性能游戏本
[动态代理-日志] 方法UpdateProduct调用完成,结果:成功
=== 代理订单管理服务 ===
[动态代理-日志] Mon May 5 14:00:00 2024
开始调用方法:CreateOrder
[订单管理] 成功创建订单:ORD20240505001
[动态代理-日志] 方法CreateOrder调用完成,结果:成功
[动态代理-日志] Mon May 5 14:00:00 2024
开始调用方法:CancelOrder
[订单管理] 成功取消订单:ORD20240505001
[动态代理-日志] 方法CancelOrder调用完成,结果:成功
20.3.5 实现方式 3:远程代理(跨进程调用模拟)
远程代理的核心是“代理类负责处理远程通信细节,客户端通过代理类透明访问远程服务器上的真实主题”。以下以“本地代理访问远程计算器服务”为例,模拟远程代理的实现(实际项目中需使用网络库如 Boost.Asio、gRPC 实现真实远程通信)。
步骤 1:定义抽象主题(远程服务接口)
#include <iostream>
#include <string>
#include <thread>
#include <chrono>
using namespace std;
// 抽象主题:计算器服务接口(远程服务)
class CalculatorService {
public:
virtual int Add(int a, int b) = 0;
virtual int Multiply(int a, int b) = 0;
virtual ~CalculatorService() {}
};
步骤 2:实现远程真实主题(模拟远程服务器)
// 真实主题:远程计算器服务(模拟运行在远程服务器)
class RemoteCalculator : public CalculatorService {
public:
int Add(int a, int b) override {
// 模拟远程服务的网络延迟
this_thread::sleep_for(chrono::milliseconds(500));
cout << "[远程服务器] 执行加法运算:" << a << " + " << b << endl;
return a + b;
}
int Multiply(int a, int b) override {
this_thread::sleep_for(chrono::milliseconds(500));
cout << "[远程服务器] 执行乘法运算:" << a << " * " << b << endl;
return a * b;
}
};
步骤 3:实现远程代理(处理通信细节)
// 远程代理:本地计算器代理(处理远程通信细节)
class CalculatorProxy : public CalculatorService {
private:
// 模拟远程服务器地址
string remoteServerAddr;
// 模拟网络连接状态
bool isConnected;
// 模拟网络连接
bool ConnectToRemote() {
cout << "[远程代理] 正在连接远程服务器:" << remoteServerAddr << endl;
// 模拟连接成功
this_thread::sleep_for(chrono::milliseconds(300));
isConnected = true;
cout << "[远程代理] 连接远程服务器成功!" << endl;
return true;
}
// 模拟数据序列化(将参数转换为网络传输格式)
string SerializeParams(int a, int b) {
return to_string(a) + "," + to_string(b);
}
// 模拟数据反序列化(将网络传输格式转换为结果)
int DeserializeResult(const string& data) {
return stoi(data);
}
public:
CalculatorProxy(const string& addr) : remoteServerAddr(addr), isConnected(false) {}
// 代理加法运算:处理连接、序列化、远程调用、反序列化
int Add(int a, int b) override {
// 1. 连接远程服务器
if (!isConnected && !ConnectToRemote()) {
throw runtime_error("连接远程服务器失败!");
}
// 2. 序列化参数
string params = SerializeParams(a, b);
cout << "[远程代理] 序列化参数:" << params << endl;
// 3. 模拟远程调用(实际项目中通过网络发送参数到服务器)
cout << "[远程代理] 发送参数到远程服务器..." << endl;
this_thread::sleep_for(chrono::milliseconds(200));
// 4. 模拟接收远程结果(实际项目中通过网络接收服务器响应)
RemoteCalculator remoteCalc; // 模拟远程服务器
int remoteResult = remoteCalc.Add(a, b);
string resultData = to_string(remoteResult);
cout << "[远程代理] 接收远程结果:" << resultData << endl;
// 5. 反序列化结果
int finalResult = DeserializeResult(resultData);
cout << "[远程代理] 反序列化结果:" << finalResult << endl;
return finalResult;
}
// 代理乘法运算:逻辑与加法一致
int Multiply(int a, int b) override {
if (!isConnected && !ConnectToRemote()) {
throw runtime_error("连接远程服务器失败!");
}
string params = SerializeParams(a, b);
cout << "[远程代理] 序列化参数:" << params << endl;
cout << "[远程代理] 发送参数到远程服务器..." << endl;
this_thread::sleep_for(chrono::milliseconds(200));
RemoteCalculator remoteCalc;
int remoteResult = remoteCalc.Multiply(a, b);
string resultData = to_string(remoteResult);
cout << "[远程代理] 接收远程结果:" << resultData << endl;
int finalResult = DeserializeResult(resultData);
cout << "[远程代理] 反序列化结果:" << finalResult << endl;
return finalResult;
}
};
步骤 4:客户端使用示例(透明访问远程服务)
int main() {
try {
// 1. 创建远程代理(客户端仅需知道服务器地址)
CalculatorService* proxy = new CalculatorProxy("192.168.1.100:8080");
// 2. 客户端通过代理透明访问远程服务(无需关心通信细节)
cout << "=== 调用远程加法服务 ===" << endl;
int addResult = proxy->Add(10, 20);
cout << "最终结果:10 + 20 = " << addResult << endl;
cout << "\n=== 调用远程乘法服务 ===" << endl;
int multiplyResult = proxy->Multiply(10, 20);
cout << "最终结果:10 * 20 = " << multiplyResult << endl;
// 3. 释放资源
delete proxy;
} catch (const exception& e) {
cout << "错误:" << e.what() << endl;
}
return 0;
}
运行结果
=== 调用远程加法服务 ===
[远程代理] 正在连接远程服务器:192.168.1.100:8080
[远程代理] 连接远程服务器成功!
[远程代理] 序列化参数:10,20
[远程代理] 发送参数到远程服务器...
[远程服务器] 执行加法运算:10 + 20
[远程代理] 接收远程结果:30
[远程代理] 反序列化结果:30
最终结果:10 + 20 = 30
=== 调用远程乘法服务 ===
[远程代理] 序列化参数:10,20
[远程代理] 发送参数到远程服务器...
[远程服务器] 执行乘法运算:10 * 20
[远程代理] 接收远程结果:200
[远程代理] 反序列化结果:200
最终结果:10 * 20 = 200
20.3.6 代理模式的优缺点与避坑指南
优点
✅ 职责分离:核心业务逻辑与辅助逻辑(权限、日志、缓存)分离,代码结构清晰;
✅ 透明访问:客户端无需感知代理的存在,可无缝替换被代理对象;
✅ 灵活扩展:可在不修改被代理对象和客户端的情况下,新增或修改辅助逻辑;
✅ 远程访问支持:通过远程代理屏蔽远程通信细节,简化分布式系统开发。
缺点
⚠️ 性能损耗:代理类的中间层会增加少量性能开销(如函数调用、序列化/反序列化);
⚠️ 代码复杂度增加:静态代理需为每个被代理类创建代理类,动态代理实现复杂;
⚠️ 调试难度提升:多层代理会增加代码调用链路,排查问题时需跟踪代理层级。
避坑指南
💡 避免过度代理:简单场景(无复杂辅助逻辑)无需使用代理模式,直接调用被代理对象;
⚠️ 静态代理适用于简单场景:辅助逻辑固定、被代理类较少时使用静态代理,否则优先选择动态代理;
💡 动态代理优先使用成熟库:实际项目中可使用 Boost.Signals2、Qt 的元对象系统或第三方动态代理库,避免重复造轮子;
💡 远程代理关注通信稳定性:真实远程代理需处理网络断开、超时重试、数据加密等问题,确保通信可靠;
💡 代理类不持有被代理对象的生命周期:被代理对象的创建和销毁由客户端负责,避免代理类与被代理类的生命周期耦合。
20.4 组合模式:树形结构的统一操作方案
组合模式(Composite Pattern)又称部分-整体模式(Part-Whole Pattern),其核心思想是“将对象组合成树形结构以表示‘部分-整体’的层次关系,使得客户端对单个对象和组合对象的使用具有一致性”。
20.4.1 核心角色与设计逻辑
1. 核心角色
- 抽象构件(Component):定义树形结构中所有节点(叶子节点和容器节点)的统一接口,包含节点的通用操作(如添加、删除、遍历);
- 叶子构件(Leaf):树形结构中的叶子节点(无子节点),实现抽象构件接口,包含叶子节点的具体业务逻辑;
- 容器构件(Composite):树形结构中的容器节点(可包含子节点),实现抽象构件接口,持有子节点列表,负责子节点的管理和统一操作。
2. 两种实现方式
- 透明组合模式:抽象构件接口包含所有节点操作(如
Add()、Remove()、GetChild()),叶子节点对不支持的操作抛出异常或空实现; - 安全组合模式:抽象构件接口仅包含叶子节点和容器节点的共有操作,
Add()、Remove()等子节点管理操作仅在容器构件中实现。
💡 实战建议:C++ 中优先使用透明组合模式,确保客户端对所有节点的操作一致性,简化客户端代码;叶子节点对不支持的操作可抛出明确异常,便于调试。
20.4.2 适用场景
- 需管理树形结构数据(如文件系统、组织架构、菜单层级、XML/JSON 文档结构);
- 希望客户端无需区分叶子节点和容器节点,统一操作整个树形结构或单个节点;
- 需对树形结构执行统一操作(如遍历所有节点、统计节点数量、计算总价值);
- 树形结构的节点类型可能扩展(如文件系统中新增“压缩文件”节点)。
20.4.3 实现方式:透明组合模式(文件系统示例)
以“文件系统管理”为例:文件系统包含文件夹(容器节点)和文件(叶子节点),文件夹可包含子文件夹和文件,客户端需统一执行遍历、统计大小、删除等操作,使用透明组合模式可实现该需求。
步骤 1:定义抽象构件(文件系统节点接口)
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
// 抽象构件:文件系统节点接口(统一所有节点的操作)
class FileSystemNode {
public:
// 构造函数:初始化节点名称
FileSystemNode(const string& name) : nodeName(name) {}
virtual ~FileSystemNode() {}
// 通用操作:获取节点名称
string GetNodeName() const {
return nodeName;
}
// 统一操作:计算节点大小(文件为自身大小,文件夹为所有子节点大小之和)
virtual int GetSize() const = 0;
// 统一操作:遍历节点(文件输出自身信息,文件夹遍历所有子节点)
virtual void Traverse(int depth = 0) const = 0;
// 子节点管理操作:添加子节点(容器节点支持,叶子节点不支持)
virtual void Add(FileSystemNode* child) {
throw runtime_error("叶子节点不支持添加子节点:" + nodeName);
}
// 子节点管理操作:删除子节点(容器节点支持,叶子节点不支持)
virtual void Remove(FileSystemNode* child) {
throw runtime_error("叶子节点不支持删除子节点:" + nodeName);
}
// 子节点管理操作:获取子节点(容器节点支持,叶子节点不支持)
virtual FileSystemNode* GetChild(int index) const {
throw runtime_error("叶子节点无可用子节点:" + nodeName);
}
protected:
string nodeName; // 节点名称(文件名或文件夹名)
};
步骤 2:实现叶子构件(文件节点)
// 叶子构件:文件节点(无子节点)
class File : public FileSystemNode {
private:
int fileSize; // 文件大小(单位:KB)
public:
// 构造函数:初始化文件名和大小
File(const string& name, int size) : FileSystemNode(name), fileSize(size) {
if (size < 0) {
throw invalid_argument("文件大小不能为负数:" + name);
}
}
// 实现统一操作:获取文件大小(自身大小)
int GetSize() const override {
cout << "文件[" << nodeName << "] 大小:" << fileSize << "KB" << endl;
return fileSize;
}
// 实现统一操作:遍历文件(输出文件信息)
void Traverse(int depth) const override {
// 输出缩进,体现树形结构层级
string indent(depth * 2, ' ');
cout << indent << "- 文件:" << nodeName << "(" << fileSize << "KB)" << endl;
}
// 叶子节点不支持子节点管理操作,使用父类的默认实现(抛出异常)
};
步骤 3:实现容器构件(文件夹节点)
// 容器构件:文件夹节点(可包含子节点)
class Folder : public FileSystemNode {
private:
// 存储子节点列表(可包含文件和子文件夹)
vector<FileSystemNode*> children;
public:
Folder(const string& name) : FileSystemNode(name) {}
// 析构函数:递归删除所有子节点(释放资源)
~Folder() {
cout << "删除文件夹:" << nodeName << endl;
for (FileSystemNode* child : children) {
delete child;
}
}
// 实现统一操作:获取文件夹大小(所有子节点大小之和)
int GetSize() const override {
int totalSize = 0;
cout << "计算文件夹[" << nodeName << "]的总大小:" << endl;
for (FileSystemNode* child : children) {
totalSize += child->GetSize();
}
cout << "文件夹[" << nodeName << "] 总大小:" << totalSize << "KB" << endl;
return totalSize;
}
// 实现统一操作:遍历文件夹(递归遍历所有子节点)
void Traverse(int depth) const override {
string indent(depth * 2, ' ');
cout << indent << "+ 文件夹:" << nodeName << endl;
// 递归遍历子节点,深度+1
for (FileSystemNode* child : children) {
child->Traverse(depth + 1);
}
}
// 实现子节点管理操作:添加子节点
void Add(FileSystemNode* child) override {
if (child == nullptr) {
throw invalid_argument("子节点不能为空!");
}
// 避免重复添加同一节点
auto iter = find(children.begin(), children.end(), child);
if (iter == children.end()) {
children.push_back(child);
cout << "文件夹[" << nodeName << "] 添加子节点:" << child->GetNodeName() << endl;
} else {
cout << "文件夹[" << nodeName << "] 已包含子节点:" << child->GetNodeName() << endl;
}
}
// 实现子节点管理操作:删除子节点
void Remove(FileSystemNode* child) override {
auto iter = find(children.begin(), children.end(), child);
if (iter != children.end()) {
cout << "文件夹[" << nodeName << "] 删除子节点:" << child->GetNodeName() << endl;
delete *iter; // 释放子节点资源
children.erase(iter);
} else {
cout << "文件夹[" << nodeName << "] 不存在子节点:" << child->GetNodeName() << endl;
}
}
// 实现子节点管理操作:获取子节点
FileSystemNode* GetChild(int index) const override {
if (index < 0 || index >= children.size()) {
throw out_of_range("子节点索引越界:" + to_string(index));
}
return children[index];
}
// 扩展操作:获取子节点数量
int GetChildCount() const {
return children.size();
}
};
步骤 4:客户端使用示例(统一操作文件系统)
int main() {
try {
// 1. 创建文件系统节点(文件夹和文件)
FileSystemNode* root = new Folder("根目录");
FileSystemNode* docFolder = new Folder("文档");
FileSystemNode* picFolder = new Folder("图片");
FileSystemNode* workFolder = new Folder("工作");
FileSystemNode* reportFile = new File("年度报告.docx", 2048);
FileSystemNode* noteFile = new File("笔记.txt", 128);
FileSystemNode* photo1 = new File("旅行.jpg", 512);
FileSystemNode* photo2 = new File("风景.png", 768);
FileSystemNode* codeFile = new File("项目代码.cpp", 4096);
// 2. 组合树形结构
root->Add(docFolder);
root->Add(picFolder);
root->Add(workFolder);
docFolder->Add(reportFile);
docFolder->Add(noteFile);
picFolder->Add(photo1);
picFolder->Add(photo2);
workFolder->Add(codeFile);
// 3. 统一操作:遍历整个文件系统
cout << "=== 遍历文件系统 ===" << endl;
root->Traverse();
// 4. 统一操作:计算根目录总大小
cout << "\n=== 计算根目录总大小 ===" << endl;
int totalSize = root->GetSize();
cout << "根目录最终总大小:" << totalSize << "KB" << endl;
// 5. 操作容器节点:删除子节点
cout << "\n=== 删除文档文件夹中的笔记.txt ===" << endl;
docFolder->Remove(noteFile);
// 6. 操作后重新遍历
cout << "\n=== 删除后遍历文件系统 ===" << endl;
root->Traverse();
// 7. 释放根节点(递归删除所有子节点)
cout << "\n=== 释放文件系统资源 ===" << endl;
delete root;
} catch (const exception& e) {
cout << "错误:" << e.what() << endl;
}
return 0;
}
运行结果
文件夹[根目录] 添加子节点:文档
文件夹[根目录] 添加子节点:图片
文件夹[根目录] 添加子节点:工作
文件夹[文档] 添加子节点:年度报告.docx
文件夹[文档] 添加子节点:笔记.txt
文件夹[图片] 添加子节点:旅行.jpg
文件夹[图片] 添加子节点:风景.png
文件夹[工作] 添加子节点:项目代码.cpp
=== 遍历文件系统 ===
+ 文件夹:根目录
+ 文件夹:文档
- 文件:年度报告.docx(2048KB)
- 文件:笔记.txt(128KB)
+ 文件夹:图片
- 文件:旅行.jpg(512KB)
- 文件:风景.png(768KB)
+ 文件夹:工作
- 文件:项目代码.cpp(4096KB)
=== 计算根目录总大小 ===
计算文件夹[根目录]的总大小:
计算文件夹[文档]的总大小:
文件[年度报告.docx] 大小:2048KB
文件[笔记.txt] 大小:128KB
文件夹[文档] 总大小:2176KB
计算文件夹[图片]的总大小:
文件[旅行.jpg] 大小:512KB
文件[风景.png] 大小:768KB
文件夹[图片] 总大小:1280KB
计算文件夹[工作]的总大小:
文件[项目代码.cpp] 大小:4096KB
文件夹[工作] 总大小:4096KB
文件夹[根目录] 总大小:7552KB
根目录最终总大小:7552KB
=== 删除文档文件夹中的笔记.txt ===
文件夹[文档] 删除子节点:笔记.txt
=== 删除后遍历文件系统 ===
+ 文件夹:根目录
+ 文件夹:文档
- 文件:年度报告.docx(2048KB)
+ 文件夹:图片
- 文件:旅行.jpg(512KB)
- 文件:风景.png(768KB)
+ 文件夹:工作
- 文件:项目代码.cpp(4096KB)
=== 释放文件系统资源 ===
删除文件夹:根目录
删除文件夹:文档
删除文件:年度报告.docx
删除文件夹:图片
删除文件:旅行.jpg
删除文件:风景.png
删除文件夹:工作
删除文件:项目代码.cpp
20.4.4 组合模式的进阶扩展:树形结构筛选与排序
在基础实现的基础上,扩展组合模式的功能,支持树形结构的节点筛选(如查找所有大于 1MB 的文件)和排序(如按节点名称排序)。
扩展 1:节点筛选功能(在抽象构件中添加筛选接口)
// 抽象构件中新增筛选接口
class FileSystemNode {
public:
// 其他原有接口...
// 筛选接口:返回符合条件的节点列表
virtual vector<FileSystemNode*> Filter(function<bool(FileSystemNode*)> condition) {
vector<FileSystemNode*> result;
// 叶子节点:自身符合条件则加入结果
if (condition(this)) {
result.push_back(this);
}
return result;
}
};
// 容器构件重写筛选接口:递归筛选所有子节点
class Folder : public FileSystemNode {
public:
// 其他原有接口...
vector<FileSystemNode*> Filter(function<bool(FileSystemNode*)> condition) override {
vector<FileSystemNode*> result;
// 文件夹自身符合条件则加入结果
if (condition(this)) {
result.push_back(this);
}
// 递归筛选子节点
for (FileSystemNode* child : children) {
vector<FileSystemNode*> childResult = child->Filter(condition);
result.insert(result.end(), childResult.begin(), childResult.end());
}
return result;
}
};
// 客户端使用筛选功能
int main() {
// 假设已创建上述文件系统root节点...
// 筛选所有大小大于1MB(1024KB)的文件
cout << "=== 筛选大小大于1MB的文件 ===" << endl;
auto largeFileCondition = [](FileSystemNode* node) {
// 判断是否为文件节点(通过动态类型转换)
File* file = dynamic_cast<File*>(node);
return file != nullptr && file->GetSize() > 1024;
};
vector<FileSystemNode*> largeFiles = root->Filter(largeFileCondition);
cout << "找到" << largeFiles.size() << "个大于1MB的文件:" << endl;
for (auto file : largeFiles) {
cout << "- " << file->GetNodeName() << endl;
}
return 0;
}
扩展 2:节点排序功能(在容器构件中添加排序接口)
class Folder : public FileSystemNode {
public:
// 其他原有接口...
// 按节点名称排序(升序)
void SortByName() {
sort(children.begin(), children.end(), [](FileSystemNode* a, FileSystemNode* b) {
return a->GetNodeName() < b->GetNodeName();
});
cout << "文件夹[" << nodeName << "] 按名称排序完成" << endl;
}
};
// 客户端使用排序功能
int main() {
// 假设已创建上述文件系统root节点...
cout << "\n=== 文件夹按名称排序后遍历 ===" << endl;
dynamic_cast<Folder*>(root)->SortByName();
root->Traverse();
return 0;
}
20.4.5 组合模式的优缺点与避坑指南
优点
✅ 统一操作接口:客户端无需区分叶子节点和容器节点,可统一操作单个节点和整个树形结构;
✅ 树形结构灵活扩展:新增节点类型(如文件系统中新增“压缩文件”)时,只需实现抽象构件接口,无需修改现有代码;
✅ 简化客户端代码:客户端无需编写复杂的树形结构遍历和管理逻辑,直接调用统一接口;
✅ 递归结构天然支持:容器节点包含子节点,天然支持递归遍历和操作,符合树形结构的特性。
缺点
⚠️ 抽象构件接口冗余:透明组合模式中,叶子节点需实现不支持的子节点管理操作,导致接口冗余;
⚠️ 节点类型判断复杂:若需对不同类型节点执行差异化操作,需通过动态类型转换判断节点类型,增加代码复杂度;
⚠️ 性能问题:深度嵌套的树形结构中,递归遍历和操作可能导致栈溢出或性能损耗。
避坑指南
💡 合理设计抽象构件接口:仅包含所有节点的共有操作,避免添加过于具体的操作,减少接口冗余;
⚠️ 叶子节点处理不支持的操作:透明组合模式中,叶子节点对 Add()、Remove() 等操作应抛出明确异常,而非静默失败;
💡 避免过深的树形结构:递归深度过深可能导致栈溢出,可通过迭代方式实现遍历和操作;
💡 结合迭代器模式:为树形结构提供迭代器,简化节点遍历逻辑,避免客户端直接操作子节点列表;
💡 资源管理:容器节点的析构函数需递归删除所有子节点,避免内存泄漏;若子节点被多个容器共享,需使用智能指针管理生命周期。
20.5 实战案例:结合代理模式与组合模式开发企业组织架构管理系统
20.5.1 需求分析
开发一个企业组织架构管理系统,核心需求如下:
- 组织架构为树形结构(公司→部门→小组→员工),支持统一操作(如遍历所有员工、统计部门人数、计算部门薪资总额);
- 员工分为普通员工和管理者,管理者可管理下属员工或小组;
- 系统需提供权限控制(如仅管理员可修改组织架构、查看薪资信息);
- 支持远程访问组织架构数据(如总部员工访问分公司组织架构);
- 支持组织架构的动态扩展(如新增分公司、部门重组)。
20.5.2 设计思路
- 组合模式:构建组织架构树形结构,抽象构件为“组织节点”,容器节点为“公司/部门/小组”,叶子节点为“普通员工”,实现统一遍历、统计操作;
- 代理模式:
- 静态代理:为组织架构操作添加权限控制(管理员可修改、查看薪资);
- 远程代理:支持远程访问分公司组织架构,屏蔽远程通信细节;
- 组合设计:代理类封装权限和远程通信逻辑,组合模式管理树形结构,客户端通过代理类统一操作组织架构。
20.5.3 完整实现代码
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
#include <ctime>
#include <thread>
#include <chrono>
#include <memory>
using namespace std;
// -------------------------- 部分1:组合模式 - 组织架构树形结构 --------------------------
// 抽象构件:组织节点接口(统一所有节点操作)
class OrganizationNode {
public:
OrganizationNode(const string& name) : nodeName(name) {}
virtual ~OrganizationNode() {}
string GetNodeName() const { return nodeName; }
// 统一操作:统计节点人数(员工返回1,部门返回所有下属人数之和)
virtual int GetPersonCount() const = 0;
// 统一操作:计算薪资总额(员工返回自身薪资,部门返回所有下属薪资之和)
virtual double CalculateTotalSalary() const = 0;
// 统一操作:遍历节点(递归输出组织架构)
virtual void Traverse(int depth = 0) const = 0;
// 子节点管理操作(容器节点支持,叶子节点不支持)
virtual void Add(OrganizationNode* child) {
throw runtime_error("叶子节点不支持添加子节点:" + nodeName);
}
virtual void Remove(OrganizationNode* child) {
throw runtime_error("叶子节点不支持删除子节点:" + nodeName);
}
virtual OrganizationNode* GetChild(int index) const {
throw runtime_error("叶子节点无可用子节点:" + nodeName);
}
protected:
string nodeName; // 节点名称(公司/部门/小组/员工姓名)
};
// 叶子构件:普通员工节点
class Employee : public OrganizationNode {
private:
double salary; // 员工薪资
string position; // 职位
public:
Employee(const string& name, const string& pos, double sal)
: OrganizationNode(name), position(pos), salary(sal) {
if (sal < 0) throw invalid_argument("薪资不能为负数:" + name);
}
int GetPersonCount() const override {
return 1; // 员工自身占1人
}
double CalculateTotalSalary() const override {
cout << "员工[" << nodeName << "] 薪资:" << salary << "元" << endl;
return salary;
}
void Traverse(int depth) const override {
string indent(depth * 2, ' ');
cout << indent << "- 员工:" << nodeName << "(职位:" << position << ",薪资:" << salary << "元)" << endl;
}
// 获取薪资(供权限代理访问)
double GetSalary() const { return salary; }
};
// 容器构件:部门节点(包含子部门或员工)
class Department : public OrganizationNode {
private:
vector<OrganizationNode*> children;
string departmentType; // 部门类型(公司/部门/小组)
public:
Department(const string& name, const string& type)
: OrganizationNode(name), departmentType(type) {}
~Department() {
cout << "删除" << departmentType << ":" << nodeName << endl;
for (auto child : children) delete child;
}
int GetPersonCount() const override {
int count = 0;
cout << "统计" << departmentType << "[" << nodeName << "]的人数:" << endl;
for (auto child : children) count += child->GetPersonCount();
cout << departmentType << "[" << nodeName << "] 总人数:" << count << endl;
return count;
}
double CalculateTotalSalary() const override {
double total = 0.0;
cout << "计算" << departmentType << "[" << nodeName << "]的薪资总额:" << endl;
for (auto child : children) total += child->CalculateTotalSalary();
cout << departmentType << "[" << nodeName << "] 薪资总额:" << total << "元" << endl;
return total;
}
void Traverse(int depth) const override {
string indent(depth * 2, ' ');
cout << indent << "+ " << departmentType << ":" << nodeName << endl;
for (auto child : children) child->Traverse(depth + 1);
}
void Add(OrganizationNode* child) override {
if (!child) throw invalid_argument("子节点不能为空!");
auto iter = find(children.begin(), children.end(), child);
if (iter == children.end()) {
children.push_back(child);
cout << departmentType << "[" << nodeName << "] 添加子节点:" << child->GetNodeName() << endl;
}
}
void Remove(OrganizationNode* child) override {
auto iter = find(children.begin(), children.end(), child);
if (iter != children.end()) {
cout << departmentType << "[" << nodeName << "] 删除子节点:" << child->GetNodeName() << endl;
delete *iter;
children.erase(iter);
}
}
OrganizationNode* GetChild(int index) const override {
if (index < 0 || index >= children.size()) throw out_of_range("子节点索引越界");
return children[index];
}
int GetChildCount() const { return children.size(); }
};
// -------------------------- 部分2:代理模式 - 权限控制与远程访问 --------------------------
// 权限控制静态代理
class OrganizationProxy : public OrganizationNode {
private:
OrganizationNode* realSubject;
string operatorRole; // 操作者角色(admin/normal)
// 权限校验
bool CheckModifyPermission() const {
cout << "[权限校验] 校验操作者角色[" << operatorRole << "]的修改权限..." << endl;
if (operatorRole == "admin") {
cout << "[权限校验] 修改权限通过" << endl;
return true;
} else {
cout << "[权限校验] 无修改权限,操作失败" << endl;
return false;
}
}
bool CheckSalaryPermission() const {
cout << "[权限校验] 校验操作者角色[" << operatorRole << "]的薪资查看权限..." << endl;
if (operatorRole == "admin") {
cout << "[权限校验] 薪资查看权限通过" << endl;
return true;
} else {
cout << "[权限校验] 无薪资查看权限,操作失败" << endl;
return false;
}
}
public:
OrganizationProxy(OrganizationNode* realNode, const string& role)
: OrganizationNode(realNode->GetNodeName()), realSubject(realNode), operatorRole(role) {
if (!realNode) throw invalid_argument("真实组织节点不能为空!");
}
~OrganizationProxy() {}
int GetPersonCount() const override {
return realSubject->GetPersonCount(); // 统计人数无需权限
}
double CalculateTotalSalary() const override {
if (!CheckSalaryPermission()) return 0.0;
return realSubject->CalculateTotalSalary();
}
void Traverse(int depth = 0) const override {
realSubject->Traverse(depth); // 遍历组织架构无需权限
}
void Add(OrganizationNode* child) override {
if (!CheckModifyPermission()) return;
realSubject->Add(child);
}
void Remove(OrganizationNode* child) override {
if (!CheckModifyPermission()) return;
realSubject->Remove(child);
}
OrganizationNode* GetChild(int index) const override {
return realSubject->GetChild(index);
}
};
// 远程代理(访问远程分公司组织架构)
class RemoteOrganizationProxy : public OrganizationNode {
private:
string remoteCompanyAddr;
bool isConnected;
bool ConnectToRemote() {
cout << "[远程代理] 正在连接远程分公司:" << remoteCompanyAddr << endl;
this_thread::sleep_for(chrono::milliseconds(500));
isConnected = true;
cout << "[远程代理] 连接成功!" << endl;
return true;
}
// 模拟远程获取组织架构数据
unique_ptr<OrganizationNode> FetchRemoteData() {
cout << "[远程代理] 正在获取远程分公司组织架构数据..." << endl;
this_thread::sleep_for(chrono::milliseconds(300));
// 模拟远程分公司组织架构
auto remoteCompany = make_unique<Department>("上海分公司", "公司");
auto techDept = new Department("技术部", "部门");
auto productDept = new Department("产品部", "部门");
techDept->Add(new Employee("李四", "高级工程师", 25000));
techDept->Add(new Employee("王五", "测试工程师", 18000));
productDept->Add(new Employee("赵六", "产品经理", 22000));
remoteCompany->Add(techDept);
remoteCompany->Add(productDept);
cout << "[远程代理] 组织架构数据获取完成!" << endl;
return remoteCompany;
}
public:
RemoteOrganizationProxy(const string& addr)
: OrganizationNode("远程分公司"), remoteCompanyAddr(addr), isConnected(false) {}
int GetPersonCount() const override {
if (!isConnected && !const_cast<RemoteOrganizationProxy*>(this)->ConnectToRemote()) {
throw runtime_error("连接远程分公司失败!");
}
auto remoteData = const_cast<RemoteOrganizationProxy*>(this)->FetchRemoteData();
return remoteData->GetPersonCount();
}
double CalculateTotalSalary() const override {
if (!isConnected && !const_cast<RemoteOrganizationProxy*>(this)->ConnectToRemote()) {
throw runtime_error("连接远程分公司失败!");
}
auto remoteData = const_cast<RemoteOrganizationProxy*>(this)->FetchRemoteData();
return remoteData->CalculateTotalSalary();
}
void Traverse(int depth = 0) const override {
if (!isConnected && !const_cast<RemoteOrganizationProxy*>(this)->ConnectToRemote()) {
throw runtime_error("连接远程分公司失败!");
}
auto remoteData = const_cast<RemoteOrganizationProxy*>(this)->FetchRemoteData();
remoteData->Traverse(depth);
}
// 远程代理不支持修改操作(仅支持查询)
void Add(OrganizationNode* child) override {
throw runtime_error("远程分公司组织架构不支持本地修改!");
}
void Remove(OrganizationNode* child) override {
throw runtime_error("远程分公司组织架构不支持本地修改!");
}
};
// -------------------------- 部分3:客户端测试与系统整合 --------------------------
int main() {
try {
// 1. 构建总公司组织架构
auto headCompany = new Department("总公司", "公司");
auto hrDept = new Department("人力资源部", "部门");
auto devDept = new Department("研发部", "部门");
auto devGroup1 = new Department("研发一组", "小组");
hrDept->Add(new Employee("张三", "HR经理", 20000));
devGroup1->Add(new Employee("孙七", "前端工程师", 19000));
devGroup1->Add(new Employee("周八", "后端工程师", 21000));
devDept->Add(devGroup1);
devDept->Add(new Employee("吴九", "架构师", 30000));
headCompany->Add(hrDept);
headCompany->Add(devDept);
// 2. 创建权限代理(管理员角色)
OrganizationProxy* adminProxy = new OrganizationProxy(headCompany, "admin");
// 3. 管理员操作:遍历总公司组织架构
cout << "=== 管理员遍历总公司组织架构 ===" << endl;
adminProxy->Traverse();
// 4. 管理员操作:计算总公司薪资总额
cout << "\n=== 管理员计算总公司薪资总额 ===" << endl;
adminProxy->CalculateTotalSalary();
// 5. 管理员操作:添加新员工
cout << "\n=== 管理员添加新员工 ===" << endl;
adminProxy->GetChild(1)->Add(new Employee("郑十", "算法工程师", 28000));
cout << "\n=== 添加后遍历研发部 ===" << endl;
adminProxy->GetChild(1)->Traverse(1);
// 6. 创建普通用户代理(无权限)
OrganizationProxy* normalProxy = new OrganizationProxy(headCompany, "normal");
cout << "\n=== 普通用户计算总公司薪资总额(无权限) ===" << endl;
normalProxy->CalculateTotalSalary();
// 7. 远程代理:访问上海分公司组织架构
RemoteOrganizationProxy* remoteProxy = new RemoteOrganizationProxy("shanghai.company.com:8080");
cout << "\n=== 远程访问上海分公司组织架构 ===" << endl;
remoteProxy->Traverse();
cout << "\n=== 远程统计上海分公司人数 ===" << endl;
remoteProxy->GetPersonCount();
// 8. 释放资源
cout << "\n=== 释放系统资源 ===" << endl;
delete remoteProxy;
delete normalProxy;
delete adminProxy;
delete headCompany;
} catch (const exception& e) {
cout << "系统错误:" << e.what() << endl;
}
return 0;
}
20.5.4 代码说明与运行效果
核心设计亮点
- 组合模式构建组织架构:通过
Department(容器节点)和Employee(叶子节点)构建树形结构,实现统一遍历、人数统计、薪资计算; - 权限代理控制访问:
OrganizationProxy为组织架构操作添加权限校验,管理员可修改架构、查看薪资,普通用户仅可查看架构; - 远程代理支持跨地域访问:
RemoteOrganizationProxy屏蔽远程通信细节,客户端可透明访问分公司组织架构; - 扩展性强:新增组织节点类型(如“分公司”“项目部”)只需实现
OrganizationNode接口,新增权限类型只需扩展代理类的校验逻辑。
运行结果(关键片段)
=== 管理员遍历总公司组织架构 ===
+ 公司:总公司
+ 部门:人力资源部
- 员工:张三(职位:HR经理,薪资:20000元)
+ 部门:研发部
+ 小组:研发一组
- 员工:孙七(职位:前端工程师,薪资:19000元)
- 员工:周八(职位:后端工程师,薪资:21000元)
- 员工:吴九(架构师,薪资:30000元)
=== 管理员计算总公司薪资总额 ===
[权限校验] 校验操作者角色[admin]的薪资查看权限...
[权限校验] 薪资查看权限通过
计算公司[总公司]的薪资总额:
计算部门[人力资源部]的薪资总额:
员工[张三] 薪资:20000元
部门[人力资源部] 薪资总额:20000元
计算部门[研发部]的薪资总额:
计算小组[研发一组]的薪资总额:
员工[孙七] 薪资:19000元
员工[周八] 薪资:21000元
小组[研发一组] 薪资总额:40000元
员工[吴九] 薪资:30000元
部门[研发部] 薪资总额:70000元
公司[总公司] 薪资总额:90000元
=== 普通用户计算总公司薪资总额(无权限) ===
[权限校验] 校验操作者角色[normal]的薪资查看权限...
[权限校验] 无薪资查看权限,操作失败
=== 远程访问上海分公司组织架构 ===
[远程代理] 正在连接远程分公司:shanghai.company.com:8080
[远程代理] 连接成功!
[远程代理] 正在获取远程分公司组织架构数据...
[远程代理] 组织架构数据获取完成!
+ 公司:上海分公司
+ 部门:技术部
- 员工:李四(职位:高级工程师,薪资:25000元)
- 员工:王五(职位:测试工程师,薪资:18000元)
+ 部门:产品部
- 员工:赵六(职位:产品经理,薪资:22000元)
20.6 本章总结
本章重点讲解了 C++ 开发中常用的两种结构型设计模式:代理模式和组合模式,核心要点总结如下:
-
代理模式:
- 核心价值:通过中间代理实现职责分离(核心业务与辅助逻辑)和对象访问控制(权限、远程访问);
- 三种核心类型:静态代理(简单场景、辅助逻辑固定)、动态代理(多类代理、辅助逻辑统一)、远程代理(跨进程/网络访问);
- 关键实现:代理类与被代理类实现同一抽象接口,通过组合方式持有被代理对象,在调用核心方法前后执行辅助逻辑;
- 适用场景:权限控制、日志记录、缓存、远程调用、资源保护。
-
组合模式:
- 核心价值:将对象组合为树形结构,实现“部分-整体”层次关系,客户端对单个节点和组合节点执行统一操作;
- 两种实现方式:透明组合模式(统一接口,推荐)、安全组合模式(接口分离,慎用);
- 关键实现:抽象构件定义统一接口,叶子节点实现核心业务,容器节点管理子节点并递归执行统一操作;
- 适用场景:树形结构管理(文件系统、组织架构、菜单层级)、统一节点操作(遍历、统计、删除)。
-
模式组合技巧:
- 代理模式 + 组合模式:代理类封装辅助逻辑(权限、远程通信),组合模式管理树形结构,客户端通过代理统一操作;
- 动态代理 + 组合模式:动态代理为树形结构的所有节点添加统一辅助逻辑(如全局日志);
- 远程代理 + 组合模式:远程代理获取远程树形结构数据,组合模式本地统一操作。
-
实战注意事项:
- 代理模式:避免过度代理、合理选择代理类型、不耦合被代理对象的生命周期;
- 组合模式:合理设计抽象接口、处理叶子节点不支持的操作、递归操作避免栈溢出、正确管理节点资源;
- 通用原则:优先组合而非继承、符合开放-封闭原则、确保接口一致性、简化客户端使用。
通过本章学习,你应能熟练运用代理模式和组合模式解决实际开发中的“对象访问控制”和“树形结构管理”问题,结合两种模式的组合使用,编写灵活、可扩展、低耦合的 C++ 代码。至此,结构型设计模式的核心内容已全部讲解完成,后续章节将进入行为型设计模式的进阶内容,进一步提升你的代码设计能力。

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



