设计模式每日硬核训练 Day 14:组合模式(Composite Pattern)完整讲解与实战应用

🔄 回顾 Day 13:桥接模式小结

在 Day 13 中,我们学习了桥接模式(Bridge Pattern):

  • 用于将"抽象"与"实现"分离,适用于双维度变化场景(如图形类型 × 渲染方式)。
  • 它强调组合替代继承,解决类爆炸问题,提升系统可扩展性。

今天我们进入一个构建层级结构的重要模式------组合模式(Composite Pattern)

组合模式的目标是:让你以一致的方式对待单个对象和对象集合(树形结构)


一、组合模式的核心动机

✅ 什么是组合模式?

组合模式用于构建树状结构的对象系统,例如:

  • 文件夹包含文件和子文件夹
  • UI 容器包含多个控件
  • 公司组织结构:员工 ← 部门 ← 公司

核心哲学:

将对象组成树形结构,客户端可以"统一操作"叶子节点与中间节点。


二、UML 结构图

复制代码
+----------------+
|   Component    |<------------------------------+
+----------------+                               |
| +operation()   |                               |
+----------------+                               |
        /\                                        |
       /  \                                       |
+-------------------+     +---------------------+ |
|  Leaf             |     |  Composite           | |
+-------------------+     +---------------------+ |
| +operation()      |     | +add(Component*)     | |
                          | +remove(Component*)  | |
                          | +operation()         | |
                          +---------------------+ |

三、角色说明

角色 职责说明
Component 抽象类,统一接口
Leaf 叶子节点,实现具体功能,不含子节点
Composite 组合节点,内部维护子组件列表

四、C++ 实现:文件系统结构

✅ 抽象组件接口

cpp 复制代码
class FileSystemNode {
public:
    virtual void display(int depth = 0) = 0;
    virtual ~FileSystemNode() = default;
};

✅ 叶子节点:文件

cpp 复制代码
class File : public FileSystemNode {
    std::string name_;
public:
    File(const std::string& name) : name_(name) {}

    void display(int depth = 0) override {
        std::cout << std::string(depth, '-') << name_ << std::endl;
    }
};

✅ 组合节点:文件夹

cpp 复制代码
class Directory : public FileSystemNode {
    std::string name_;
    std::vector<std::unique_ptr<FileSystemNode>> children_;

public:
    Directory(const std::string& name) : name_(name) {}

    void add(std::unique_ptr<FileSystemNode> node) {
        children_.emplace_back(std::move(node));
    }

    void display(int depth = 0) override {
        std::cout << std::string(depth, '-') << name_ << "/" << std::endl;
        for (const auto& child : children_) {
            child->display(depth + 2);
        }
    }
};

✅ 使用示例

cpp 复制代码
int main() {
    auto root = std::make_unique<Directory>("root");
    root->add(std::make_unique<File>("file1.txt"));

    auto subDir = std::make_unique<Directory>("subdir");
    subDir->add(std::make_unique<File>("file2.txt"));
    subDir->add(std::make_unique<File>("file3.txt"));

    root->add(std::move(subDir));
    root->display();
    return 0;
}

输出:

复制代码
root/
--file1.txt
--subdir/
----file2.txt
----file3.txt

五、组合模式适用场景

场景 对象树结构说明
操作系统文件系统 文件 + 文件夹,操作接口统一
图形界面控件 窗口、容器、按钮、文本框构成控件树
公司组织架构 CEO → 部门主管 → 员工
报表结构层级 表头、表体、表尾、字段
HTML DOM 树 节点 + 元素 + 属性

六、优点与缺点总结

✅ 优点:

  • 统一接口,客户端无差别调用
  • 树结构天然适合层次建模
  • 扩展方便,添加新节点只需实现 Component

❗ 缺点:

  • 违背接口隔离原则:叶子节点和组合节点共用接口,部分函数空实现
  • 调试复杂,结构越深越难定位问题

七、与装饰器 / 责任链等模式对比

模式 核心区别 类似点
Composite 结构树形,有聚合子对象 Component 接口统一
Decorator 功能增强,包裹单一对象 接口一致、动态组合
Chain 责任链传递,节点决定是否继续传递 多节点连接,共同参与处理

八、面试回答模板

"在我们的配置中心中,使用组合模式构建配置节点树,既可以是叶子属性(字段),也可以是组合节点(嵌套组)。所有节点都继承自统一接口,使我们可以用递归统一地遍历配置结构、序列化、验证,代码简洁且扩展性好。"

✅ 建议突出树形结构、递归遍历、统一调用等优势。


九、记忆口诀

"树形结构走统一,组合调用不分离;叶子整体皆一类,层层嵌套递归易。"


十、明日预告:Day 15

享元模式(Flyweight Pattern):通过共享技术减少对象数量,提升内存利用效率。

相关推荐
碎梦归途15 小时前
23种设计模式-行为型模式之模板方法模式(Java版本)
java·开发语言·jvm·设计模式·软考·模板方法模式·软件设计师
YoseZang1 天前
【设计模式】GoF设计模式之策略模式(Strategy Pattern)
设计模式·策略模式
世纪摆渡人1 天前
设计模式-策略模式(Strategy Pattern)
设计模式·策略模式
yangyang_z1 天前
【C++设计模式之Observer观察者模式】
c++·观察者模式·设计模式
Hxyle2 天前
c++设计模式
开发语言·c++·设计模式
摘星编程2 天前
并发设计模式实战系列(17):信号量(Semaphore)
设计模式·并发编程
琢磨先生David2 天前
Java 企业级开发设计模式全解析
java·设计模式
不当菜虚困2 天前
JAVA设计模式——(十一)建造者模式(Builder Pattern)
java·设计模式·建造者模式
codefly-xtl2 天前
责任链设计模式
java·开发语言·设计模式
冼紫菜2 天前
如何使用责任链模式优雅实现功能(滴滴司机、家政服务、请假审批等)
java·开发语言·设计模式·责任链模式