组合模式
组合模式用于处理树形结构,让客户端可以用统一方式操作“单个对象”和“对象集合”。
一句话理解:把对象组织成树,叶子节点和组合节点使用同一个接口。
解决什么问题
很多业务天然是树形结构:
- 部门和员工。
- 菜单和子菜单。
- 文件夹和文件。
- 分类和商品分类。
- 权限节点和子权限。
如果客户端要区分叶子节点和父节点,代码会充满判断:
java
if (node is folder) {
for child...
} else {
handle file
}组合模式把共同操作抽象成统一接口,让客户端递归处理即可。
基本结构
text
Component
^
|
Leaf
Composite -> List<Component>角色:
Component:统一接口。Leaf:叶子节点,没有子节点。Composite:组合节点,包含多个子节点。
Java 示例
菜单节点接口:
java
interface MenuComponent {
String name();
void print(String prefix);
}叶子菜单:
java
class MenuItem implements MenuComponent {
private final String name;
MenuItem(String name) {
this.name = name;
}
public String name() {
return name;
}
public void print(String prefix) {
System.out.println(prefix + "- " + name);
}
}菜单分组:
java
import java.util.ArrayList;
import java.util.List;
class MenuGroup implements MenuComponent {
private final String name;
private final List<MenuComponent> children = new ArrayList<>();
MenuGroup(String name) {
this.name = name;
}
public MenuGroup add(MenuComponent child) {
children.add(child);
return this;
}
public String name() {
return name;
}
public void print(String prefix) {
System.out.println(prefix + "+ " + name);
for (MenuComponent child : children) {
child.print(prefix + " ");
}
}
}使用:
java
MenuGroup root = new MenuGroup("系统管理")
.add(new MenuItem("用户管理"))
.add(new MenuItem("角色管理"))
.add(new MenuGroup("权限管理")
.add(new MenuItem("菜单权限"))
.add(new MenuItem("接口权限")));
root.print("");使用场景
- 树形菜单。
- 文件目录。
- 组织架构。
- 分类树。
- 权限树。
- 表达式树。
优点
- 客户端统一处理叶子和组合对象。
- 递归遍历结构清晰。
- 新增节点类型比较方便。
- 适合表达部分和整体关系。
缺点
- 过度统一可能让接口包含叶子节点不需要的方法。
- 树太深时要注意递归深度和性能。
- 如果节点差异很大,强行统一会让模型别扭。
透明式和安全式
透明式组合把 add/remove 放在公共接口中,客户端简单,但叶子节点也会暴露不适合的方法。
安全式组合只在组合节点中提供 add/remove,模型更严谨,客户端可能需要知道节点类型。
实际开发中更常用安全式组合。
实战注意
组合模式的核心不是“用了递归”,而是让客户端以统一接口处理树中的所有节点。