组合模式:将对象组合成树形结构以表示 “部分‑整体” 的层次结构,使得用户对单个对象和组合对象的使用具有一致性。 角色:
抽象构件 (Component):统一叶子、容器的公共接口
叶子构件 (Leaf):叶子节点,没有子节点
容器构件 (Composite):包含子节点,可以管理子元素
Client:客户端,统一调用 Component 接口,不用区分叶子还是组合
import java.util.ArrayList;
import java.util.List;
/**
* 抽象构件 Component:文件系统抽象,文件和文件夹都实现该接口
*/
public abstract class FileSystemNode {
protected String name;
public FileSystemNode(String name) {
this.name = name;
}
// 打印信息
public abstract void show(int depth);
// 容器才支持增删子节点,叶子不支持,提供默认抛出异常
public void add(FileSystemNode node) {
throw new UnsupportedOperationException("叶子节点不支持add操作");
}
public void remove(FileSystemNode node) {
throw new UnsupportedOperationException("叶子节点不支持remove操作");
}
public FileSystemNode getChild(int index) {
throw new UnsupportedOperationException("叶子节点不支持getChild操作");
}
}
/**
* Leaf 叶子构件:文件,没有子节点
*/
class FileLeaf extends FileSystemNode {
public FileLeaf(String name) {
super(name);
}
@Override
public void show(int depth) {
// 打印缩进模拟层级
for (int i = 0; i < depth; i++) {
System.out.print("--");
}
System.out.println("📄 文件:" + name);
}
}
/**
* Composite 容器构件:文件夹,可以包含文件/子文件夹
*/
class FolderComposite extends FileSystemNode {
private final List<FileSystemNode> children = new ArrayList<>();
public FolderComposite(String name) {
super(name);
}
@Override
public void add(FileSystemNode node) {
children.add(node);
}
@Override
public void remove(FileSystemNode node) {
children.remove(node);
}
@Override
public FileSystemNode getChild(int index) {
return children.get(index);
}
@Override
public void show(int depth) {
for (int i = 0; i < depth; i++) {
System.out.print("--");
}
System.out.println("📂 文件夹:" + name);
// 递归打印所有子节点
for (FileSystemNode child : children) {
child.show(depth + 1);
}
}
}
/**
* 客户端测试
*/
class Client {
public static void main(String[] args) {
// 构建根文件夹
FolderComposite root = new FolderComposite("根目录");
// 叶子文件
FileLeaf file1 = new FileLeaf("笔记.txt");
FileLeaf file2 = new FileLeaf("照片.jpg");
// 子文件夹
FolderComposite subFolder = new FolderComposite("文档");
FileLeaf file3 = new FileLeaf("报告.docx");
// 组装树形结构
root.add(file1);
root.add(file2);
root.add(subFolder);
subFolder.add(file3);
// 统一调用show,不需要区分是文件还是文件夹
root.show(0);
System.out.println("\n--- 删除照片.jpg后 ---");
root.remove(file2);
root.show(0);
}
}
输出结果
📂 文件夹:根目录
--📄 文件:笔记.txt
--📄 文件:照片.jpg
--📂 文件夹:文档
----📄 文件:报告.docx
--- 删除照片.jpg后 ---
📂 文件夹:根目录
--📄 文件:笔记.txt
--📂 文件夹:文档
----📄 文件:报告.docx
两种实现方式说明
透明模式(上面示例)
将
add/remove/getChild放在抽象父类Component中;叶子类重写抛出异常。客户端不需要区分叶子 / 组合,完全面向抽象;缺点:叶子拥有无意义的方法。
安全模式
add/remove/getChild只在Composite文件夹中定义;Component 只定义公共业务方法show()。优点:叶子不会暴露不需要的方法;缺点客户端需要强制类型转换才能操作子节点。
安全模式简单改动示例
// Component只保留show
public abstract class FileSystemNode {
protected String name;
public FileSystemNode(String name){this.name=name;}
public abstract void show(int depth);
}
// Composite容器才有add/remove
class FolderComposite extends FileSystemNode{
public void add(FileSystemNode node){...}
}
// 客户端使用时需要强转
if(node instanceof FolderComposite folder){
folder.add(new FileLeaf("xxx.txt"));
}
适用场景
需要表示对象整体‑部分树形结构,如文件目录、组织架构、菜单树
希望客户端忽略组合对象与单个对象差异,统一使用
优缺点
✅ 优点:
高层调用简单,统一对待叶子和组合对象;
易于扩展,新增叶子 / 容器,原有代码无需修改(开闭原则)。
❌ 缺点:
透明模式下叶子会继承无用方法;
安全模式需要类型判断,代码繁琐;
层级很深时,调试复杂。
本文原创作者:易君召,详见:https://www.yijunzhao.cc/about,转载请注明出处。
原文链接
欢迎访问