1. 简介

本文将带你快速掌握 Java 中的组合设计模式(Composite Pattern)

这个模式在处理树形结构时特别实用,比如组织架构、文件系统、UI 组件树等。它的核心思想是:✅ 让你统一处理单个对象和对象的组合,无需关心当前操作的是“叶子”还是“容器”。

掌握它,能帮你写出更清晰、扩展性更强的代码,尤其是在构建层级结构时,避免一堆 instanceof 判断和 if-else 踩坑。

2. 模式结构

组合模式的本质是一个树形结构,其中所有节点都实现同一个接口或继承同一个抽象类,从而对外提供一致的操作方式。

它通常由以下四个角色组成:

  • Component(组件):基础接口或抽象类,定义所有节点共有的行为(如操作子节点、获取信息等)。客户端通过它来统一操作所有对象。
  • Leaf(叶子):最底层的节点,没有子节点。它实现 Component 接口,但不会包含其他 Component。
  • Composite(组合):容器节点,可以包含多个子节点(Leaf 或其他 Composite)。它实现 Component 接口,并管理子节点的增删、遍历等操作。
  • Client(客户端):通过 Component 接口与整个结构交互,无需关心具体是 Leaf 还是 Composite。

✅ 核心优势:客户端代码完全透明,调用 printDepartmentName() 时,不管是调用 HeadDepartment 还是 SalesDepartment,代码写法一致。

3. 实战示例

我们以公司部门的层级结构为例:总公司(HeadDepartment)下辖多个子部门(如 SalesDepartment、FinancialDepartment),而子部门不再包含其他部门。

3.1 组件接口(Component)

定义所有部门共有的行为:

public interface Department {
    void printDepartmentName();
}

这个接口就是所有部门的“统一入口”,不管是单个部门还是部门集合,都必须实现它。

3.2 叶子节点(Leaf)

叶子部门是最基础的单位,不包含子部门。

财务部示例:

public class FinancialDepartment implements Department {

    private Integer id;
    private String name;

    public FinancialDepartment(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public void printDepartmentName() {
        System.out.println(getClass().getSimpleName());
    }

    // getters and setters
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

销售部示例:

public class SalesDepartment implements Department {

    private Integer id;
    private String name;

    public SalesDepartment(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public void printDepartmentName() {
        System.out.println(getClass().getSimpleName());
    }

    // getters and setters
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

⚠️ 注意:这两个类是典型的 Leaf,它们不持有其他 Department 实例,只实现自己的行为。

3.3 组合节点(Composite)

HeadDepartment 作为总公司,可以管理多个子部门:

import java.util.ArrayList;
import java.util.List;

public class HeadDepartment implements Department {
    private Integer id;
    private String name;
    private List<Department> childDepartments;

    public HeadDepartment(Integer id, String name) {
        this.id = id;
        this.name = name;
        this.childDepartments = new ArrayList<>();
    }

    public void printDepartmentName() {
        childDepartments.forEach(Department::printDepartmentName);
    }

    public void addDepartment(Department department) {
        childDepartments.add(department);
    }

    public void removeDepartment(Department department) {
        childDepartments.remove(department);
    }

    // getters and setters
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

✅ 关键点:

  • childDepartments 存的是 Department 接口,实现了多态。
  • printDepartmentName() 不是自己打印,而是委托给所有子节点去打印,这就是递归遍历的起点。
  • 提供了 add / remove 方法,用于动态维护树结构。

4. 测试验证

写个简单的 Demo 来验证组合行为是否正常:

public class CompositeDemo {
    public static void main(String[] args) {
        Department salesDepartment = new SalesDepartment(
            1, "Sales department");
        Department financialDepartment = new FinancialDepartment(
            2, "Financial department");

        HeadDepartment headDepartment = new HeadDepartment(
            3, "Head department");

        headDepartment.addDepartment(salesDepartment);
        headDepartment.addDepartment(financialDepartment);

        headDepartment.printDepartmentName();
    }
}

输出结果:

SalesDepartment
FinancialDepartment

✅ 成功!调用 headDepartment.printDepartmentName() 后,自动遍历并打印了所有子部门的名称。客户端代码无需知道内部是叶子还是容器,简单粗暴地调用即可。

5. 总结

组合设计模式通过统一接口,将“个体”与“整体”无缝衔接,特别适合处理树形结构。

它的优势在于:

  • ✅ 客户端代码简洁,透明操作所有节点
  • ✅ 扩展性强,新增 Leaf 或 Composite 不影响现有逻辑
  • ✅ 符合开闭原则和单一职责原则

当然也要注意:

  • ❌ 不适合所有树结构,如果 Leaf 和 Composite 行为差异太大,强行统一反而复杂
  • ❌ 管理复杂层级时,需注意循环引用问题

项目源码已托管至 GitHub:https://github.com/baeldung/design-patterns-structural


原始标题:Composite Design Pattern in Java | Baeldung