设计模式(八)组合模式 — 以树结构统一管理对象层级

🌳 一、引言:当对象呈树状生长时,管理就需要"组合"

在实际开发中,我们经常遇到需要处理层级结构的场景:

  • 文件系统(文件夹中包含文件或子文件夹)
  • UI 组件树(窗口 → 面板 → 按钮)
  • 公司组织架构(部门 → 子部门 → 员工)
  • 菜单系统(菜单 → 子菜单 → 菜单项)

这些结构都具有相同特点:

  1. 整体与部分需要统一处理
  2. 层级可能无限嵌套
  3. 不同类型节点应该具备一致的访问方式

这正是**组合模式(Composite Pattern)**发挥作用的地方。

组合模式的核心思想:

使用统一的组件接口,使"整体对象"和"部分对象"在结构上保持一致,从而让客户端可以以一致的方式操作层级结构。


🧩 二、核心概念:Component / Leaf / Composite

组合模式中包含三类角色:

角色 描述
Component(抽象组件) 定义公共接口,约束所有节点的行为
Leaf(叶子节点) 代表最小单元,不再包含子节点
Composite(组合节点) 可以包含子节点,也实现与 Component 相同的接口

Component +operation() Leaf +operation() Composite -children: List +add(Component) +remove(Component) +operation()

组合模式让客户端可以统一对待叶子节点和组合节点


🗂️ 三、经典示例:文件系统的统一管理

以文件系统为例:

  • 文件夹(Composite)可以包含文件或子文件夹
  • 文件(Leaf)属于最基本单元
  • 用户可以对文件或文件夹执行相同操作:如展示名称、统计大小、删除等

组合模式特别适合这种递归结构


🧱 四、Java 示例:构建层级结构并统一操作

java 复制代码
// 抽象组件
public abstract class FileComponent {
    protected String name;

    public FileComponent(String name) {
        this.name = name;
    }

    public abstract void display();

    public void add(FileComponent component) {
        throw new UnsupportedOperationException();
    }

    public void remove(FileComponent component) {
        throw new UnsupportedOperationException();
    }
}

// 叶子节点
public class FileLeaf extends FileComponent {
    public FileLeaf(String name) {
        super(name);
    }

    @Override
    public void display() {
        System.out.println("📄 File: " + name);
    }
}

// 组合节点
import java.util.ArrayList;
import java.util.List;

public class FolderComposite extends FileComponent {
    private List<FileComponent> children = new ArrayList<>();

    public FolderComposite(String name) {
        super(name);
    }

    @Override
    public void add(FileComponent component) {
        children.add(component);
    }

    @Override
    public void remove(FileComponent component) {
        children.remove(component);
    }

    @Override
    public void display() {
        System.out.println("📁 Folder: " + name);
        for (FileComponent c : children) {
            c.display();
        }
    }
}

// 使用示例
public class Client {
    public static void main(String[] args) {
        FolderComposite root = new FolderComposite("root");
        FolderComposite doc = new FolderComposite("doc");
        FolderComposite img = new FolderComposite("img");

        root.add(doc);
        root.add(img);

        doc.add(new FileLeaf("resume.docx"));
        doc.add(new FileLeaf("design.pdf"));

        img.add(new FileLeaf("logo.png"));
        img.add(new FileLeaf("banner.jpg"));

        root.display();
    }
}

输出:

console 复制代码
📁 Folder: root
📁 Folder: doc
📄 File: resume.docx
📄 File: design.pdf
📁 Folder: img
📄 File: logo.png
📄 File: banner.jpg

客户端完全无需区分文件夹和文件。


🧭 五、使用场景

组合模式适用于:

  • 需要表示 树形结构
  • 整体与部分需要一致处理
  • 结构具有 递归性
  • 操作需要对树中所有节点执行
  • 操作需要对树中所有节点执行(如 render、delete、calculate)

典型例子:

  • DOM 结构
  • 菜单系统
  • 公司组织架构
  • 游戏场景树(Unity、UE)

🔁 六、统一操作如何实现?

客户端只需调用:

java 复制代码
component.operation();

无论 component 是:

  • 一个简单对象(Leaf)
  • 一个树结构(Composite)
  • 甚至是一个空节点

它都遵循相同接口,这便带来了极大的灵活性。

在这种设计中,有两种方式处理 add/remove:

① 透明方式

Component 中声明 add/remove(不管是否叶子)

优点:客户端完全一致操作

缺点:叶子节点的方法会无意义

② 安全方式

只有 Composite 提供 add/remove

优点:语义清晰

缺点:客户端需判断是否为 Composite

组合模式根据需要选择透明或安全方式。


🔍 七、与其他模式的关系

组合模式常与以下模式一起出现:

✔ 1. 迭代器模式(Iterator)

用于遍历树形结构。

✔ 2. 责任链模式(Chain of Responsibility)

节点层级可向父节点传递消息。

✔ 3. 访问者模式(Visitor)

对树中所有节点施加新的操作。

✔ 4. 享元模式(Flyweight)

某些叶子节点可被共享,减少内存。

组合模式是构建复杂结构体系的"基础骨架"。


🛠️ 八、实现技巧

1. 孩子列表可延迟初始化

减少内存开销:

java 复制代码
private List<FileComponent> children; // 需要时才 new

2. Composite 必须实现所有 Component 方法

确保调用一致性。

3. operation 内可决定是否遍历子节点

如 UI 渲染中可根据 visible 属性优化。

4. 通常配合多态实现递归

递归是组合模式的灵魂。


🎮 九、实战:游戏场景树

例如 UI 控件:

  • 叶子(Button)可能有 click 动作
  • 容器(Panel)可能有 layout 布局

此时需要:

  • 共享接口作为 Component
  • 各自扩展行为不在 Component 中声明

组合模式并不要求所有方法一致,只是要求核心功能一致


📐 十、复杂 UI 渲染结构

Root Node Layout Node Style Node Render Node Children Layout Children Render

父节点负责:

  • 计算布局
  • 应用样式
  • 触发渲染

子节点生成子图层,最终合成一棵可渲染树。


🧲 十一、批量操作示例

在树中执行批处理,例如:

  • 统计所有节点数量
  • 查找节点
  • 删除节点
  • 批量刷新

操作基本套路:

java 复制代码
void operation() {
    // 当前节点的操作
    ...

    // 递归对子节点操作
    for (Component c : children) {
        c.operation();
    }
}

组合模式的"统一入口"设计让递归操作非常自然。


📐 十二、优缺点

优点

  • 统一处理整体与部分
  • 扩展性强
  • 递归结构更易管理
  • 操作可作用于整棵树

缺点

  • 系统结构可能过于通用
  • 叶子暴露无意义方法
  • 调试复杂(深层递归)

🎯 十三、总结

组合模式让我们能够:

  • 自然地处理树结构
  • 统一管理对象层级
  • 对整个结构执行批处理操作

是 UI、游戏引擎、组织架构、文件系统等领域的核心模式。

相关推荐
TDengine (老段)1 小时前
TDengine 转换函数 CAST 用户手册
java·大数据·数据库·物联网·时序数据库·tdengine·涛思数据
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ1 小时前
java实现校验sql中,表字段在表里是否都存在,不存在的给删除掉
java·sql
编程火箭车1 小时前
【Java SE 基础学习打卡】15 分隔符、标识符与关键字
java·java入门·标识符·关键字·编程基础·分隔符·语法规则
灰色人生qwer1 小时前
idea teminal和 window cmd 输出java version不一致
java·ide·intellij-idea
WayneJoon.H2 小时前
Java反序列化 CC7链分析
java·安全·网络安全·cc链·反序列化
liu_bees2 小时前
Jenkins 中修改 admin 账号密码的正确位置与方法
java·运维·tomcat·jenkins
世洋Blog2 小时前
开发思想-(数据驱动+组合模式)VS 继承
unity·组合模式·数据驱动
明洞日记2 小时前
【设计模式手册011】享元模式 - 共享细粒度对象的高效之道
java·设计模式·享元模式
世洋Blog2 小时前
开发思想-组合模式和接口多态的一点思考
c#·组合模式