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

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

在这里插入图片描述

20.1 本章学习目标与重点

💡 掌握代理模式的核心设计思想、三种核心类型(静态代理、动态代理、远程代理)及 C++ 实现细节
💡 理解组合模式的树形结构设计逻辑、透明组合与安全组合的区别及适用场景
💡 能够结合实际业务场景(如权限控制、资源代理、树形结构管理)灵活运用两种模式
💡 解决模式应用中的关键问题(如代理模式的性能损耗、组合模式的节点遍历效率)
重点:代理模式的职责隔离逻辑、组合模式的统一节点操作技巧、两种模式的组合使用场景

20.2 设计模式进阶认知:结构型模式的扩展价值

在前一章装饰者模式与适配器模式的基础上,本章继续深入结构型设计模式的核心应用。结构型模式的核心价值不仅在于“组合对象”,更在于通过合理的结构设计,实现“职责分离”“层级管理”等高级目标。

20.2.1 为什么需要代理模式与组合模式?

实际开发中,我们常遇到以下复杂场景问题:

  • 需为某个对象提供“中间层”(如权限校验、日志记录、资源缓存),但不想修改对象本身的代码(如第三方库对象、核心业务对象);
  • 需访问远程对象(如分布式系统中的服务),但不想暴露远程调用的复杂细节(如网络连接、数据序列化);
  • 需管理树形结构数据(如文件系统、组织架构、菜单层级),但希望对单个节点和整个树结构执行统一操作(如遍历、删除、统计);
  • 树形结构中节点类型多样(如文件系统中的文件和文件夹),但希望客户端无需区分节点类型即可统一处理。

代理模式通过“中间代理”实现职责分离,组合模式通过“统一接口”实现树形结构的层级管理,两者分别解决“对象访问控制”和“树形结构操作”的核心问题。

20.2.2 结构型模式的进阶设计原则

结合本章两种模式的特性,需重点牢记以下进阶设计原则:

  1. 职责分离原则:将核心功能与辅助功能(如权限、日志、缓存)分离,通过代理类封装辅助功能;
  2. 统一接口原则:树形结构中所有节点(叶子节点、容器节点)实现统一接口,确保客户端操作一致性;
  3. 透明性原则:代理类与被代理类、叶子节点与容器节点对外暴露一致的接口,客户端无需感知差异;
  4. 最小知识原则:客户端只需与代理类、树形结构的根节点交互,无需了解内部实现细节。

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 需求分析

开发一个企业组织架构管理系统,核心需求如下:

  1. 组织架构为树形结构(公司→部门→小组→员工),支持统一操作(如遍历所有员工、统计部门人数、计算部门薪资总额);
  2. 员工分为普通员工和管理者,管理者可管理下属员工或小组;
  3. 系统需提供权限控制(如仅管理员可修改组织架构、查看薪资信息);
  4. 支持远程访问组织架构数据(如总部员工访问分公司组织架构);
  5. 支持组织架构的动态扩展(如新增分公司、部门重组)。

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++ 开发中常用的两种结构型设计模式:代理模式和组合模式,核心要点总结如下:

  1. 代理模式:

    • 核心价值:通过中间代理实现职责分离(核心业务与辅助逻辑)和对象访问控制(权限、远程访问);
    • 三种核心类型:静态代理(简单场景、辅助逻辑固定)、动态代理(多类代理、辅助逻辑统一)、远程代理(跨进程/网络访问);
    • 关键实现:代理类与被代理类实现同一抽象接口,通过组合方式持有被代理对象,在调用核心方法前后执行辅助逻辑;
    • 适用场景:权限控制、日志记录、缓存、远程调用、资源保护。
  2. 组合模式:

    • 核心价值:将对象组合为树形结构,实现“部分-整体”层次关系,客户端对单个节点和组合节点执行统一操作;
    • 两种实现方式:透明组合模式(统一接口,推荐)、安全组合模式(接口分离,慎用);
    • 关键实现:抽象构件定义统一接口,叶子节点实现核心业务,容器节点管理子节点并递归执行统一操作;
    • 适用场景:树形结构管理(文件系统、组织架构、菜单层级)、统一节点操作(遍历、统计、删除)。
  3. 模式组合技巧:

    • 代理模式 + 组合模式:代理类封装辅助逻辑(权限、远程通信),组合模式管理树形结构,客户端通过代理统一操作;
    • 动态代理 + 组合模式:动态代理为树形结构的所有节点添加统一辅助逻辑(如全局日志);
    • 远程代理 + 组合模式:远程代理获取远程树形结构数据,组合模式本地统一操作。
  4. 实战注意事项:

    • 代理模式:避免过度代理、合理选择代理类型、不耦合被代理对象的生命周期;
    • 组合模式:合理设计抽象接口、处理叶子节点不支持的操作、递归操作避免栈溢出、正确管理节点资源;
    • 通用原则:优先组合而非继承、符合开放-封闭原则、确保接口一致性、简化客户端使用。

通过本章学习,你应能熟练运用代理模式和组合模式解决实际开发中的“对象访问控制”和“树形结构管理”问题,结合两种模式的组合使用,编写灵活、可扩展、低耦合的 C++ 代码。至此,结构型设计模式的核心内容已全部讲解完成,后续章节将进入行为型设计模式的进阶内容,进一步提升你的代码设计能力。

评论 35
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值