组合模式(Composite Pattern)

C++ 组合模式(Composite Pattern)

一、模式基础概述

1. 模式定义

组合模式属于结构型设计模式 ,核心是将多个对象构建成树形层级结构 ,用来表达整体-部分 的业务关系。该模式让单个叶子对象包含多个子元素的组合容器对象具备一致的调用接口,客户端无需区分对象类型,以统一方式遍历、调用、管理树形结构中所有节点。

2. 核心设计思想

  1. 行为一致性:叶子节点与组合节点继承同一抽象父类,对外提供统一行为接口
  2. 树形层级构建:通过组合节点嵌套子节点,无限层级搭建业务树状结构
  3. 递归统一处理:对顶层组合对象执行操作,可自动递归下发至所有底层叶子节点
  4. 弱化类型判断 :消除客户端大量if/else类型判断代码,简化调用逻辑
  5. 遵循设计原则 :符合开闭原则单一职责原则,容器负责聚合管理,叶子负责具体业务

3. 三大核心角色详解

角色名称 英文名称 核心职责 C++ 实现规范
抽象组件 Component 定义所有节点通用抽象接口,声明通用业务方法与子节点管理方法 纯虚基类,声明虚析构函数,统一对外行为
叶子组件 Leaf 树形结构最末端节点,无子节点,实现具体业务功能 继承抽象组件,重写业务方法,空实现增删子节点方法
组合容器 Composite 容器节点,内部存储子组件集合,实现子节点增删查改,递归调度子节点行为 继承抽象组件,维护容器数组,实现聚合遍历逻辑

4. 两大实现方案对比

(1)透明组合模式(企业开发首选)
  • 设计规则:抽象组件基类中统一声明 add、remove、获取子节点等容器管理方法
  • 实现差异:组合节点正常实现管理方法,叶子节点空实现或抛出异常
  • 优点:客户端调用完全统一,无需强制类型转换,代码简洁
  • 缺点:叶子节点存在无用接口,轻微违反接口隔离原则
(2)安全组合模式
  • 设计规则:仅在组合容器类中声明子节点管理方法,抽象组件只保留通用业务接口
  • 优点:接口职责划分清晰,无冗余无效方法,安全性高
  • 缺点:客户端必须判断节点类型,强转后才能操作子节点,破坏调用统一性

二、模式适用与禁用场景

适用场景

  1. 业务存在整体与部分层级关系,需要树形结构管理数据
  2. 客户端希望无差别统一操作单个对象与批量组合对象
  3. 典型业务:文件目录系统、公司组织架构、UI界面控件树、菜单层级、权限树形结构、商品分类树
  4. 需要批量递归执行操作:批量统计、批量渲染、批量执行任务、批量权限校验

不适用场景

  1. 业务无层级嵌套关系,仅存在平级简单对象
  2. 叶子节点存在大量独有专属行为,无法统一抽象接口
  3. 树形层级极深,递归调用易造成栈溢出的高性能场景
  4. 对象结构固定,后期无新增节点扩展需求

三、组合模式优缺点

优点

  1. 层级结构表达直观,完美适配所有树形业务模型
  2. 客户端代码极简,彻底消除类型判断逻辑,降低使用难度
  3. 扩展性极强:新增叶子节点、新增组合容器均无需修改原有核心代码
  4. 批量操作便捷:依靠递归实现一键遍历全树节点,高效完成批量业务
  5. 代码复用性高:通用抽象接口可在全项目通用,节点组件可灵活组合复用

缺点

  1. 架构设计复杂度提升,需要提前抽象统一接口,前期设计成本高
  2. 透明模式下叶子节点包含冗余接口,接口设计不够严谨
  3. 树形结构层级过深时,递归遍历存在性能开销与栈溢出风险
  4. 难以精准控制单个叶子节点的独有个性化行为,统一接口限制灵活性

四、C++ 核心编码实现规范

  1. 所有组件统一继承抽象虚基类 ,必须声明virtual ~类名()虚析构,防止多态内存泄漏
  2. 优先使用std::shared_ptr智能指针管理所有节点,自动回收内存,避免野指针与内存泄漏
  3. 组合容器内部使用vector动态数组存储子组件,支持动态增删节点
  4. 通用业务方法统一在基类定义,容器聚合遍历逻辑仅在组合类实现
  5. 严格区分职责:叶子只做业务实现,组合只做子节点管理与递归调度

五、C++ 经典实战代码示例

示例1:文件目录系统(透明组合模式完整版)

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std;

// 抽象组件:文件系统统一基类
class FileSystemNode
{
protected:
    string nodeName;
public:
    FileSystemNode(string name) : nodeName(name) {}
    virtual ~FileSystemNode() = default;

    // 统一通用业务接口
    virtual void showStructure(int space = 0) const = 0;
    virtual long long getTotalSize() const = 0;

    // 透明模式:统一声明子节点管理方法
    virtual void addChild(shared_ptr<FileSystemNode> node) {}
    virtual void removeChild(const string& name) {}
};

// 叶子节点:文件
class FileLeaf : public FileSystemNode
{
private:
    long long fileSize;
public:
    FileLeaf(string name, long long size) : FileSystemNode(name), fileSize(size) {}

    void showStructure(int space = 0) const override
    {
        string blank(space, ' ');
        cout << blank << "文件:" << nodeName << " 大小:" << fileSize << "KB" << endl;
    }

    long long getTotalSize() const override
    {
        return fileSize;
    }
};

// 组合容器:文件夹
class FolderComposite : public FileSystemNode
{
private:
    vector<shared_ptr<FileSystemNode>> childList;
public:
    FolderComposite(string name) : FileSystemNode(name) {}

    void showStructure(int space = 0) const override
    {
        string blank(space, ' ');
        cout << blank << "文件夹:" << nodeName << endl;
        // 递归遍历所有子节点
        for (const auto& node : childList)
        {
            node->showStructure(space + 4);
        }
    }

    long long getTotalSize() const override
    {
        long long total = 0;
        for (const auto& node : childList)
        {
            total += node->getTotalSize();
        }
        return total;
    }

    // 实现子节点添加
    void addChild(shared_ptr<FileSystemNode> node) override
    {
        childList.push_back(node);
    }

    // 实现子节点删除
    void removeChild(const string& name) override
    {
        for (auto iter = childList.begin(); iter != childList.end(); ++iter)
        {
            if ((*iter)->nodeName == name)
            {
                childList.erase(iter);
                cout << "成功移除节点:" << name << endl;
                return;
            }
        }
        cout << "未找到目标节点:" << name << endl;
    }
};

// 客户端测试
int main()
{
    // 构建叶子文件
    auto txtFile = make_shared<FileLeaf>("笔记.txt", 512);
    auto imgFile = make_shared<FileLeaf>("风景图.png", 20480);
    auto docFile = make_shared<FileLeaf>("工作总结.docx", 1024);

    // 构建层级文件夹
    auto officeFolder = make_shared<FolderComposite>("办公文件");
    auto imageFolder = make_shared<FolderComposite>("图片素材");
    auto rootFolder = make_shared<FolderComposite>("本地磁盘D");

    // 组装树形结构
    officeFolder->addChild(txtFile);
    officeFolder->addChild(docFile);
    imageFolder->addChild(imgFile);

    rootFolder->addChild(officeFolder);
    rootFolder->addChild(imageFolder);

    // 统一调用展示目录结构
    cout << "===== 完整文件目录结构 =====" << endl;
    rootFolder->showStructure();

    // 统一调用统计总大小
    cout << "\n磁盘总占用空间:" << rootFolder->getTotalSize() << " KB" << endl;

    // 移除指定子节点
    rootFolder->removeChild("图片素材");
    return 0;
}

示例2:企业组织架构简易实现

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std;

// 抽象组织组件
class OrganizationComponent
{
protected:
    string orgName;
public:
    OrganizationComponent(string name) : orgName(name) {}
    virtual ~OrganizationComponent() = default;
    virtual void showInfo() const = 0;
    virtual int getTotalStaff() const = 0;
    virtual void addMember(shared_ptr<OrganizationComponent> comp) {}
};

// 叶子节点:普通员工
class StaffLeaf : public OrganizationComponent
{
public:
    StaffLeaf(string name) : OrganizationComponent(name) {}
    void showInfo() const override
    {
        cout << "普通员工:" << orgName << endl;
    }
    int getTotalStaff() const override
    {
        return 1;
    }
};

// 组合容器:部门
class DepartmentComposite : public OrganizationComponent
{
private:
    vector<shared_ptr<OrganizationComponent>> memberList;
public:
    DepartmentComposite(string name) : OrganizationComponent(name) {}

    void showInfo() const override
    {
        cout << "==== 所属部门:" << orgName << " ====" << endl;
        for (auto& m : memberList)
        {
            m->showInfo();
        }
    }

    int getTotalStaff() const override
    {
        int count = 0;
        for (auto& m : memberList) count += m->getTotalStaff();
        return count;
    }

    void addMember(shared_ptr<OrganizationComponent> comp) override
    {
        memberList.push_back(comp);
    }
};

// 客户端调用
int main()
{
    auto emp1 = make_shared<StaffLeaf>("张三");
    auto emp2 = make_shared<StaffLeaf>("李四");
    auto techDept = make_shared<DepartmentComposite>("技术研发部");

    techDept->addMember(emp1);
    techDept->addMember(emp2);

    techDept->showInfo();
    cout << "部门总人数:" << techDept->getTotalStaff() << " 人" << endl;
    return 0;
}

六、组合模式与相近设计模式区分

1. 组合模式 vs 外观模式

  • 组合模式:聚焦树形层级结构 ,统一操作整体与部分,侧重层级递归管理
  • 外观模式:聚焦多子系统整合 ,提供统一访问入口,侧重简化复杂调用流程

2. 组合模式 vs 桥接模式

  • 组合模式:构建对象整体-部分树形关系,侧重对象聚合组合
  • 桥接模式:拆分抽象与实现两大维度,侧重双向独立扩展,解决多维度类爆炸

3. 组合模式 vs 装饰器模式

  • 组合:多个同级对象聚合嵌套,形成树形结构,数量不固定
  • 装饰器:单个对象层层包裹,动态增强对象功能,为链式嵌套结构

七、C++ 项目开发最佳实践

  1. 项目目录分层规范

    CompositePattern/
    ├─ ComponentBase.h // 抽象组件基类头文件
    ├─ LeafNode.h // 所有叶子节点类
    └─ CompositeNode.h // 所有组合容器类

  2. 对外业务层仅依赖抽象组件基类,屏蔽具体叶子与组合实现细节

  3. 树形结构构建统一在业务层完成,组件内部只负责自身逻辑

  4. 对于层级极深的树形结构,迭代遍历替代递归遍历,规避栈溢出

  5. 严格控制组合容器权限,禁止在叶子节点中编写容器管理相关逻辑

  6. 大型项目中可搭配迭代器模式,统一遍历树形所有节点

八、模式核心总结

组合模式核心精髓:一父统两类,树形构层级,调用无区分,递归统执行

通过抽象基类统一规范所有节点行为,利用组合容器聚合子节点,完美解决程序中所有整体与部分的层级业务场景,是C++开发中实现树形数据结构、层级管理业务最标准、最常用的结构型设计模式。

相关推荐
likerhood4 天前
设计模式 · 组合模式(Composite Pattern)
设计模式·组合模式
蜡笔小马6 天前
07.C++设计模式-组合模式
c++·设计模式·组合模式
多加点辣也没关系7 天前
设计模式-组合模式
设计模式·组合模式
qq_2965532711 天前
[特殊字符] 数组中的递增三元组:O(n) 时间高效查找,面试必考!
数据结构·算法·面试·职场和发展·组合模式·柔性数组
geovindu1 个月前
go: Composite Pattern
设计模式·golang·组合模式
ximu_polaris1 个月前
设计模式(C++)-结构型模式-组合模式
c++·设计模式·组合模式
yaaakaaang1 个月前
八、组合模式
组合模式
无籽西瓜a1 个月前
【西瓜带你学设计模式 | 第十三期 - 组合模式】组合模式 —— 树形结构统一处理实现、优缺点与适用场景
java·后端·设计模式·组合模式·软件工程
砍光二叉树2 个月前
【设计模式】结构型-组合模式
设计模式·组合模式