组合模式
组合模式,允许创建树型层次结构来改变复杂性,同时允许结构中的每一个元素操作同一个接口。该模式将对象组合成树型结构来表示会整个或部分的层次结构。这就意味着组合模式允许客户端使用单个对象或多个同一对象的组合。

主要角色
抽象构件(Component)角色:定义了所有节点(包括叶子节点和树枝节点)的共有接口或抽象类。 提供了访问和管理子组件的方法,但在安全组合模式下可能不直接提供这些方法。 可能实现了一些默认行为,所有子类都继承并可覆写这些行为。
叶子构件(Leaf)角色:组合中的叶节点对象,表示树结构中最基础的元素。 不包含任何子节点,因此实现了抽象构件接口时,其对应的方法可能为空或者抛出异常,表明它不能添加或删除子节点。 它们通常包含与自身相关的业务逻辑。
树枝构件(Composite)角色 / 中间构件:组合中的分支节点对象,它具有子节点,并且能够存储和管理这些子节点。 实现了抽象构件接口,提供了添加、删除以及遍历子节点的方法。除了自身的业务逻辑之外,还负责协调其子节点的行为。
代码实现
抽象构件
抽象构建中定义节点的抽象,包含节点的访问方法。
public abstract class Component {
public abstract void operation();
public abstract void add(Component c);
public abstract void remove(Component c);
public abstract Component getChild(int i);
}叶子节点
叶子节点是抽象构建的实现,它不含有子节点。
public class Leaf extends Component {
@Override
public void operation() {
// Implement operation
System.out.println("Leaf operation");
}
@Override
public Component getChild(int index) {
throw new UnsupportedOperationException("Leaf node has no children");
}
@Override
public void remove(Component component) {
throw new UnsupportedOperationException("Cannot remove from leaf node");
}
@Override
public void add(Component component) {
throw new UnsupportedOperationException("Cannot add to leaf node");
}
}树枝节点
树枝节点是抽象构建的实现,它可以含有多个子节点。这里使用List<Component> components来存放所有的子节点。 并且通过实现抽象组件的方法来实现对子节点的访问和操作。
public class Composite extends Component {
public String name;
private List<Component> components = new ArrayList<>();
public Composite(String name) {
this.name = name;
}
@Override
public void operation() {
System.out.println(String.format("【%s】Composite operation", this.name));
for (Component component : this.components) {
component.operation();
}
}
@Override
public Component getChild(int index) {
if (index < 0 || index >= this.components.size()) {
throw new IndexOutOfBoundsException("Index out of bounds");
}
return this.components.get(index);
}
@Override
public void remove(Component component) {
if (this.components.contains(component)) {
this.components.remove(component);
} else {
throw new IllegalArgumentException("Component not found");
}
}
@Override
public void add(Component component) {
if (this.components.contains(component)) {
throw new IllegalArgumentException("Component already exists");
} else {
this.components.add(component);
}
}
}使用案例
public class CompositeClient {
public static void main(String[] args) {
// 创建一个根节点
Composite composite = new Composite("root");
composite.add(new Composite("child1"));
composite.add(new Composite("child2"));
composite.add(new Leaf());
composite.operation();
}
}上面的代码中,首先创建了一个根节点root,然后根节点有三个子节点,分别是child1,child2和leaf。 然后调用根节点的operation方法,会打印出根节点和所有子节点的操作信息。
结果: 
与其他设计模式的对比
组合模式 vs 装饰器模式
| 特性 | 组合模式 | 装饰器模式 |
|---|---|---|
| 目的 | 构建树形结构,统一处理单个对象和组合对象 | 动态扩展对象功能 |
| 结构 | 节点可以包含子节点,形成树形结构 | 装饰器包装原对象,保持接口一致 |
| 关注点 | 整体与部分的层次关系 | 功能的动态扩展 |
| 扩展性 | 高(可动态添加或移除节点) | 高(可动态添加多个装饰器) |
| 适用场景 | 需要处理树形结构的场景 | 需要动态扩展对象功能而不修改原类 |
组合模式 vs 适配器模式
| 特性 | 组合模式 | 适配器模式 |
|---|---|---|
| 目的 | 构建树形结构,统一处理单个对象和组合对象 | 转换接口,使不兼容的类能够协同工作 |
| 结构 | 节点可以包含子节点,形成树形结构 | 适配器作为两个不兼容接口之间的桥梁 |
| 关注点 | 整体与部分的层次关系 | 接口的转换 |
| 扩展性 | 高(可动态添加或移除节点) | 中(主要解决接口不兼容问题) |
| 适用场景 | 需要处理树形结构的场景 | 需要集成已有类,但接口不兼容时 |
组合模式 vs 门面模式
| 特性 | 组合模式 | 门面模式 |
|---|---|---|
| 目的 | 构建树形结构,统一处理单个对象和组合对象 | 为复杂系统提供简单的接口 |
| 结构 | 节点可以包含子节点,形成树形结构 | 门面类封装复杂系统的交互逻辑 |
| 关注点 | 整体与部分的层次关系 | 系统的简化接口 |
| 扩展性 | 高(可动态添加或移除节点) | 中(主要简化接口,不改变系统结构) |
| 适用场景 | 需要处理树形结构的场景 | 需要简化复杂系统接口的场景 |
总结
组合模式是一种结构型设计模式,它将对象组合成树形结构以表示"部分-整体"的层次关系,使得用户对单个对象和组合对象的使用具有一致性。
核心思想
将对象组合成树形结构,通过统一的接口来处理单个对象和组合对象,使得客户端无需区分它们的差异,可以一致地对待所有对象。
主要角色
- 抽象构件(Component):定义了所有节点(包括叶子节点和树枝节点)的共有接口或抽象类,提供了访问和管理子组件的方法。
- 叶子构件(Leaf):组合中的叶节点对象,表示树结构中最基础的元素,不包含任何子节点。
- 树枝构件(Composite):组合中的分支节点对象,它具有子节点,并且能够存储和管理这些子节点,负责协调其子节点的行为。
实现方式
- 透明组合模式:在抽象构件中声明所有管理子构件的方法,包括添加、删除和获取子构件的方法,这样所有构件类都具有相同的接口。
- 安全组合模式:只在树枝构件中声明管理子构件的方法,叶子构件没有这些方法,这样可以避免在叶子构件中调用不支持的方法。
优点
- 统一接口:客户端可以一致地对待单个对象和组合对象,无需区分它们的差异。
- 简化客户端代码:客户端无需关心对象的层次结构和具体类型,可以直接调用统一的接口。
- 易于扩展:可以方便地添加新的节点类型,而无需修改客户端代码,符合开闭原则。
- 灵活性高:可以动态地组合对象,形成不同的树形结构。
缺点
- 可能违反单一职责原则:抽象构件可能需要同时负责管理子构件和实现自身的业务逻辑。
- 设计复杂:需要合理设计抽象构件和具体构件之间的关系,避免层次结构过于复杂。
- 类型安全性降低:透明组合模式中,客户端可能会在叶子节点上调用不支持的方法,需要在运行时进行错误处理。
适用场景
- 需要处理树形结构的场景:如文件系统、组织结构、菜单等。
- 需要统一处理单个对象和组合对象的场景:客户端希望以相同的方式处理简单对象和复杂对象。
- 需要动态组合对象的场景:需要在运行时动态地添加或移除节点。
实际应用
在实际开发中,组合模式常用于:
- 文件系统的实现(文件夹和文件的层次结构)
- GUI组件的设计(窗口、面板、按钮等的层次结构)
- 组织结构的表示(公司、部门、员工的层次结构)
- 菜单系统的实现(主菜单、子菜单、菜单项的层次结构)
- XML/HTML文档的解析(标签和文本的层次结构)