Skip to content

组合模式处理"树形结构"——文件和文件夹、菜单和子菜单、部门和子部门。它让"单个对象"和"组合对象"对外表现一致,客户端不用区分。这篇讲清它怎么统一处理叶子和树枝。

一、解决什么问题:树形结构的统一处理

文件系统是典型的树形结构:文件夹里可以有文件,也可以有子文件夹。客户端想"遍历整个目录",得区分"这是文件(处理它)"还是"文件夹(递归进去)":

java
// 不用组合模式:客户端要判断类型,代码一堆 if
if (node instanceof File) {
    ((File) node).read();
} else if (node instanceof Folder) {
    for (Node child : ((Folder) node).getChildren()) { ... }  // 递归
}

组合模式的思路:让"文件"和"文件夹"实现同一个接口,客户端统一调用,不用区分。

二、核心结构

java
// 抽象组件:文件和文件夹都实现它
interface FileSystemNode {
    void print(String indent);
}

// 叶子:文件
class File implements FileSystemNode {
    private String name;
    public void print(String indent) { System.out.println(indent + name); }
}

// 树枝:文件夹,持有子节点列表
class Folder implements FileSystemNode {
    private String name;
    private List<FileSystemNode> children = new ArrayList<>();

    void add(FileSystemNode node) { children.add(node); }

    public void print(String indent) {
        System.out.println(indent + name + "/");
        for (FileSystemNode child : children) {
            child.print(indent + "  ");   // 递归,但不用判断是文件还是文件夹
        }
    }
}

关键Folder.print() 里递归调用 child.print(),但不用判断 child 是 File 还是 Folder——因为它们都是 FileSystemNode,统一调 print 就行。

三、核心价值:统一"整体"和"部分"

组合模式让"单个对象"(文件)和"组合对象"(文件夹)对外表现一致,客户端可以把它们一视同仁:

  • 客户端不用写 if (是文件) ... else if (是文件夹) ...
  • 加新节点类型(比如"快捷方式"),客户端代码不用改

四、实际应用

  • 文件系统:文件和目录
  • 菜单系统:菜单项(叶子)和子菜单(树枝)
  • UI 组件树:按钮(叶子)和面板(树枝,能装其他组件)

小结

  • 组合模式处理树形结构,让"叶子"和"树枝"实现同一接口
  • 客户端统一调用,不用区分单个还是组合
  • 典型场景:文件系统、菜单、UI 组件树

相关:《责任链模式》(同为链式结构,但组合是树、责任链是链)。