文章目录
- 前言
- 一、概念
- 二、核心结构
- [三、Java 代码实现(文件 + 文件夹示例)](#三、Java 代码实现(文件 + 文件夹示例))
-
- [1. 抽象组件(Component)](#1. 抽象组件(Component))
- [2. 叶子节点:文件(Leaf)](#2. 叶子节点:文件(Leaf))
- [3. 组合节点:文件夹(Composite)](#3. 组合节点:文件夹(Composite))
- [4. 客户端测试](#4. 客户端测试)
- 四、组合模式的两种形式
-
- [1. 透明模式(本文示例)](#1. 透明模式(本文示例))
- [2. 安全模式](#2. 安全模式)
- 五、优缺点
- 六、应用场景
- [七、组合模式 VS 外观模式](#七、组合模式 VS 外观模式)
- 八、总结
前言
在业务系统中,我们经常会遇到**"整体与部分"具有 一致性操作的场景:比如文件与文件夹、菜单与子菜单、部门与员工、订单与订单明细、树形权限等。如果用两套代码分别处理"单个对象"和"组合对象",代码会臃肿、难维护、极易出错。组合模式正是用来解决这类树形结构、整体与部分统一处理**的经典设计模式。
一、概念
组合模式(Composite Pattern) 是一种结构型设计模式 ,核心思想是:
将对象组合成树形结构,以表示"整体-部分"的层次关系,并且让客户端可以统一对待单个对象和组合对象。
简单理解:
- 叶子节点(Leaf):最小单元,无子节点(如文件、单个员工)
- 组合节点(Composite):包含子节点,可以是叶子或其他组合(如文件夹、部门)
- 客户端:不用判断是叶子还是组合,直接调用同一套接口
二、核心结构
- Component(抽象组件)
定义统一接口,叶子和组合都实现它。 - Leaf(叶子节点)
最小单元,没有子节点,实现业务逻辑。 - Composite(组合节点)
包含子节点列表,实现添加/删除/遍历子节点逻辑。 - Client(客户端)
面向 Component 编程,统一调用。
三、Java 代码实现(文件 + 文件夹示例)
以最经典的文件系统为例:
- 文件(Leaf):不能再包含子节点
- 文件夹(Composite):可以包含文件或文件夹
- 统一方法:show() 显示信息
1. 抽象组件(Component)
java
public abstract class FileSystemComponent {
protected String name;
public FileSystemComponent(String name) {
this.name = name;
}
// 统一展示方法
public abstract void show();
// 组合类专用,叶子类空实现或抛异常
public void add(FileSystemComponent component) {
throw new UnsupportedOperationException("不支持添加");
}
public void remove(FileSystemComponent component) {
throw new UnsupportedOperationException("不支持删除");
}
}
2. 叶子节点:文件(Leaf)
java
public class File extends FileSystemComponent {
public File(String name) {
super(name);
}
@Override
public void show() {
System.out.println("文件:" + name);
}
}
3. 组合节点:文件夹(Composite)
java
import java.util.ArrayList;
import java.util.List;
public class Folder extends FileSystemComponent {
private List<FileSystemComponent> children = new ArrayList<>();
public Folder(String name) {
super(name);
}
@Override
public void add(FileSystemComponent component) {
children.add(component);
}
@Override
public void remove(FileSystemComponent component) {
children.remove(component);
}
@Override
public void show() {
System.out.println("文件夹:" + name);
// 递归展示所有子节点
for (FileSystemComponent child : children) {
child.show();
}
}
}
4. 客户端测试
java
public class Client {
public static void main(String[] args) {
// 叶子
FileSystemComponent file1 = new File("笔记.txt");
FileSystemComponent file2 = new File("图片.jpg");
FileSystemComponent file3 = new File("设计稿.psd");
// 组合
FileSystemComponent folder1 = new Folder("学习资料");
folder1.add(file1);
folder1.add(file2);
FileSystemComponent folder2 = new Folder("工作文档");
folder2.add(file3);
folder2.add(folder1); // 嵌套组合
// 统一调用 show()
folder2.show();
}
}
输出:
文件夹:工作文档
设计稿.psd
文件夹:学习资料
笔记.txt
图片.jpg
四、组合模式的两种形式
1. 透明模式(本文示例)
- Component 定义 add/remove/show
- 叶子也必须实现,默认抛异常或空实现
- 优点:客户端完全不用区分叶子/组合,真正透明
- 缺点:叶子有多余方法,违反单一职责
2. 安全模式
- Component 只定义公共方法(如 show)
- add/remove 只在 Composite 中定义
- 优点:安全,不会调用错方法
- 缺点:客户端要判断类型,失去统一处理优势
实际开发:透明模式更常用。
五、优缺点
优点
- 统一处理整体与部分
客户端不用判断是单个对象还是组合对象。 - 完美适配树形结构
菜单、组织架构、权限、目录、订单套餐等天然适配。 - 扩展性极强
新增叶子/组合类不影响旧代码,符合开闭原则。 - 简化客户端代码
不用写大量 if-else 判断类型。
缺点
- 设计略复杂,需要区分 Leaf 和 Composite
- 若层次太深,递归遍历可能影响性能
- 透明模式会让叶子拥有不该有的方法(add/remove)
六、应用场景
凡是树形结构、整体与部分能统一操作的场景,都用组合模式:
- 文件系统:文件 + 文件夹
- 菜单系统:菜单、子菜单、菜单项
- 组织架构:公司 → 部门 → 小组 → 员工
- 权限系统:角色 → 权限组 → 权限点
- 订单/购物车:订单 → 商品组 → 商品
- UI 组件树:页面 → 容器 → 组件
经典源码应用:
- Java AWT / Swing 组件树(Container + Component)
- MyBatis 动态 SQL 节点(
SqlNode组合) - Spring Security 权限配置
七、组合模式 VS 外观模式
| 组合模式 | 外观模式 |
|---|---|
| 整体-部分树形结构 | 复杂子系统统一入口 |
| 递归、嵌套、层次 | 一层封装、转发 |
| 客户端统一处理单个/组合 | 客户端只访问入口,不接触子系统 |
- 组合:内部结构是树形,强调"部分+整体一致"
- 外观:内部结构复杂,强调"简化入口"
八、总结
- 组合模式 = 树形结构 + 整体与部分统一处理
- 核心角色:Component(抽象)、Leaf(叶子)、Composite(组合)
- 核心价值:客户端不用判断类型,统一调用
- 最适合:菜单、文件、组织架构、权限、订单等树形结构
- 开发中极常用,是处理层级结构的最优解