C++ 组合模式(Composite Pattern)
一、模式基础概述
1. 模式定义
组合模式属于结构型设计模式 ,核心是将多个对象构建成树形层级结构 ,用来表达整体-部分 的业务关系。该模式让单个叶子对象 和包含多个子元素的组合容器对象具备一致的调用接口,客户端无需区分对象类型,以统一方式遍历、调用、管理树形结构中所有节点。
2. 核心设计思想
- 行为一致性:叶子节点与组合节点继承同一抽象父类,对外提供统一行为接口
- 树形层级构建:通过组合节点嵌套子节点,无限层级搭建业务树状结构
- 递归统一处理:对顶层组合对象执行操作,可自动递归下发至所有底层叶子节点
- 弱化类型判断 :消除客户端大量
if/else类型判断代码,简化调用逻辑 - 遵循设计原则 :符合开闭原则 、单一职责原则,容器负责聚合管理,叶子负责具体业务
3. 三大核心角色详解
| 角色名称 | 英文名称 | 核心职责 | C++ 实现规范 |
|---|---|---|---|
| 抽象组件 | Component | 定义所有节点通用抽象接口,声明通用业务方法与子节点管理方法 | 纯虚基类,声明虚析构函数,统一对外行为 |
| 叶子组件 | Leaf | 树形结构最末端节点,无子节点,实现具体业务功能 | 继承抽象组件,重写业务方法,空实现增删子节点方法 |
| 组合容器 | Composite | 容器节点,内部存储子组件集合,实现子节点增删查改,递归调度子节点行为 | 继承抽象组件,维护容器数组,实现聚合遍历逻辑 |
4. 两大实现方案对比
(1)透明组合模式(企业开发首选)
- 设计规则:抽象组件基类中统一声明 add、remove、获取子节点等容器管理方法
- 实现差异:组合节点正常实现管理方法,叶子节点空实现或抛出异常
- 优点:客户端调用完全统一,无需强制类型转换,代码简洁
- 缺点:叶子节点存在无用接口,轻微违反接口隔离原则
(2)安全组合模式
- 设计规则:仅在组合容器类中声明子节点管理方法,抽象组件只保留通用业务接口
- 优点:接口职责划分清晰,无冗余无效方法,安全性高
- 缺点:客户端必须判断节点类型,强转后才能操作子节点,破坏调用统一性
二、模式适用与禁用场景
适用场景
- 业务存在整体与部分层级关系,需要树形结构管理数据
- 客户端希望无差别统一操作单个对象与批量组合对象
- 典型业务:文件目录系统、公司组织架构、UI界面控件树、菜单层级、权限树形结构、商品分类树
- 需要批量递归执行操作:批量统计、批量渲染、批量执行任务、批量权限校验
不适用场景
- 业务无层级嵌套关系,仅存在平级简单对象
- 叶子节点存在大量独有专属行为,无法统一抽象接口
- 树形层级极深,递归调用易造成栈溢出的高性能场景
- 对象结构固定,后期无新增节点扩展需求
三、组合模式优缺点
优点
- 层级结构表达直观,完美适配所有树形业务模型
- 客户端代码极简,彻底消除类型判断逻辑,降低使用难度
- 扩展性极强:新增叶子节点、新增组合容器均无需修改原有核心代码
- 批量操作便捷:依靠递归实现一键遍历全树节点,高效完成批量业务
- 代码复用性高:通用抽象接口可在全项目通用,节点组件可灵活组合复用
缺点
- 架构设计复杂度提升,需要提前抽象统一接口,前期设计成本高
- 透明模式下叶子节点包含冗余接口,接口设计不够严谨
- 树形结构层级过深时,递归遍历存在性能开销与栈溢出风险
- 难以精准控制单个叶子节点的独有个性化行为,统一接口限制灵活性
四、C++ 核心编码实现规范
- 所有组件统一继承抽象虚基类 ,必须声明
virtual ~类名()虚析构,防止多态内存泄漏 - 优先使用
std::shared_ptr智能指针管理所有节点,自动回收内存,避免野指针与内存泄漏 - 组合容器内部使用
vector动态数组存储子组件,支持动态增删节点 - 通用业务方法统一在基类定义,容器聚合遍历逻辑仅在组合类实现
- 严格区分职责:叶子只做业务实现,组合只做子节点管理与递归调度
五、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++ 项目开发最佳实践
-
项目目录分层规范
CompositePattern/
├─ ComponentBase.h // 抽象组件基类头文件
├─ LeafNode.h // 所有叶子节点类
└─ CompositeNode.h // 所有组合容器类 -
对外业务层仅依赖抽象组件基类,屏蔽具体叶子与组合实现细节
-
树形结构构建统一在业务层完成,组件内部只负责自身逻辑
-
对于层级极深的树形结构,迭代遍历替代递归遍历,规避栈溢出
-
严格控制组合容器权限,禁止在叶子节点中编写容器管理相关逻辑
-
大型项目中可搭配迭代器模式,统一遍历树形所有节点
八、模式核心总结
组合模式核心精髓:一父统两类,树形构层级,调用无区分,递归统执行
通过抽象基类统一规范所有节点行为,利用组合容器聚合子节点,完美解决程序中所有整体与部分的层级业务场景,是C++开发中实现树形数据结构、层级管理业务最标准、最常用的结构型设计模式。