组合模式的核心是让叶子节点和容器节点对外呈现一致接口,客户端无需判断类型即可统一调用;通过定义统一抽象组件接口,所有节点实现相同操作与管理方法,采用透明式或安全式设计,leaf仅实现行为不管理子节点,composite负责递归委托,客户端无感操作整棵树。

组合模式的核心是让叶子节点和树形结构中的容器节点对外呈现完全一致的接口,客户端无需判断类型就能统一调用。关键不在于“能不能”,而在于“怎么设计接口”和“谁该负责哪些行为”。
定义统一的抽象组件接口
所有节点——无论是文件、按钮这类最小单元,还是文件夹、面板这类可嵌套容器——都实现同一个接口。这个接口声明操作行为(如 display()、getSize()),也声明管理行为(如 add()、remove())。
常见做法有两种:
- 透明式:接口中包含全部方法(包括管理子节点的方法),叶子节点对 add/remove 等操作抛出 UnsupportedOperationException 或直接忽略
- 安全式:接口只保留通用行为(如 display、getSize),把 add/remove 等管理方法移到 Composite 类中,Leaf 不实现它们
推荐初学者从透明式入手,代码更简洁;生产环境若需强类型安全,可选安全式。
叶子节点只做本职,不管理子节点
Leaf 类代表不可再分的最小单元,比如单个文件、一个按钮、一条菜单项。它必须实现接口中所有行为方法,但对管理类方法不做实际支持。
例如:
- File.getSize() 返回自身大小
- File.display(2) 按缩进打印 “- File: readme.txt (12.5 KB)”
- File.add(...) 抛异常或静默失败(取决于透明/安全风格)
组合节点负责递归与委托
Composite 类内部维护一个子组件列表,它自己也实现同一接口。当客户端调用 display() 或 getSize() 时,它不是自己计算结果,而是遍历所有子节点,把请求转发下去。
典型逻辑:
- display(indent):先打印自身名称,再对每个 child 调用 display(indent + 2)
- getSize():累加所有子节点的 getSize() 结果
- add(component):将 component 加入 children 列表
- remove(component):从 children 中移除指定 component
客户端完全无感地操作整棵树
客户端拿到的始终是 FileSystemComponent 或 UIComponent 类型引用,不管是传入一个 File、一个 Folder,还是一整个 DesktopPanel,代码写法一模一样:
- root.display(0); —— 自动展开所有层级
- root.getSize(); —— 自动递归求和
- root.add(new File("log.txt", 4.2)); —— 若 root 是 Composite 就成功,是 Leaf 就失败(按设计策略)
不需要 instanceof,不写 if-else 分支,也不关心当前对象底下有没有孩子。











