文章目录
- 0.个人感悟
- [1. 概念](#1. 概念)
- [2. 适配场景](#2. 适配场景)
-
- [2.1 适合的场景](#2.1 适合的场景)
- [2.2 常见场景举例](#2.2 常见场景举例)
- [3. 实现方法](#3. 实现方法)
-
- [3.1 实现思路](#3.1 实现思路)
- [3.2 UML类图](#3.2 UML类图)
- [3.3 代码示例](#3.3 代码示例)
- [4. 优缺点](#4. 优缺点)
-
- [4.1 优点](#4.1 优点)
- [4.2 缺点](#4.2 缺点)
- [5. 源码分析(JDK中的组合模式实现)](#5. 源码分析(JDK中的组合模式实现))
0.个人感悟
- 组合模式的应用场景比较专,适合树状嵌套场景,统一去处理单个对象和组合对象
- 可以扩展学习学习数据结构中的tree
- 使用的时候注意简单对象和组合对象的一致性,如果差异大,不建议使用
1. 概念
英文定义 (《设计模式:可复用面向对象软件的基础》)
Compose objects into tree structures to represent part-whole hierarchies. Composite
lets client treat individual objects and compositions of objects uniformly.
中文翻译
将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
理解
- 组合模式的核心思想是用一致的方式处理单个对象和组合对象
- 通过树形结构组织对象,形成"部分-整体"的层次关系
- 客户端无需关心当前处理的是单个对象还是组合对象,简化了客户端代码
2. 适配场景
2.1 适合的场景
- 需要表示对象的"部分-整体"层次结构(如文件系统、组织架构)
- 希望客户端忽略组合对象与单个对象的差异,统一地使用结构中的所有对象
- 需要对树形结构中的所有节点执行统一操作(如统计、渲染、搜索)
- 系统需要动态地添加或删除组件,且组件可能嵌套多层
2.2 常见场景举例
- GUI界面开发:窗口包含面板,面板包含按钮、文本框等控件
- 文件系统:文件夹可以包含文件或子文件夹
- 组织架构:部门包含员工和子部门
- 菜单系统:菜单包含菜单项或子菜单
- 图形编辑器:复杂图形由简单图形组合而成
3. 实现方法
3.1 实现思路
- 定义抽象组件(Component)接口或抽象类:声明所有对象的共同接口,包括管理子组件的方法(添加、删除、获取子组件)
- 创建叶子(Leaf)类:实现Component接口,表示树中的叶子节点(没有子节点)
- 创建组合(Composite)类:实现Component接口,包含子组件集合,并实现子组件的管理方法
- 客户端通过Component接口与所有对象交互,无需区分是单个对象还是组合对象
3.2 UML类图

角色说明:
- Component(抽象组件):定义组合对象和叶子对象的共同接口
- Leaf(叶子):没有子组件的简单对象
- Composite(组合):包含子组件的复杂对象,存储和管理子组件
3.3 代码示例
背景:电脑文件系统,支持展开梳妆结构,其中叶子结点是文件,复杂对象是文件夹
定义统一的文件系统组件:
java
public abstract class FileSystemComponent {
protected String name;
public FileSystemComponent(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
}
/**
* @description display
* @author bigHao
* @date 2026/1/11
**/
public abstract void display();
/**
* @param component 组件
* @description 添加操作 文件不支持
* @author bigHao
* @date 2026/1/11
**/
public void add(FileSystemComponent component) {
throw new UnsupportedOperationException("不支持添加操作");
}
/**
* @param component 组件
* @description 移除操作,文件不支持
* @author bigHao
* @date 2026/1/11
**/
public void remove(FileSystemComponent component) {
throw new UnsupportedOperationException("不支持删除操作");
}
/**
* @param index 索引
* @return FileSystemComponent 子对象
* @description 获取子对象 文件不支持
* @author bigHao
* @date 2026/1/11
**/
public FileSystemComponent getChild(int index) {
throw new UnsupportedOperationException("不支持获取子组件操作");
}
}
叶子节点:
java
public class File extends FileSystemComponent {
private String extension;
public File(String name, String extension) {
super(name);
this.extension = extension;
}
@Override
public void display() {
System.out.println(name + "(" + extension + ")");
}
public String getExtension() {
return extension;
}
public void setExtension(String extension) {
this.extension = extension;
}
}
组合节点: 注意children属性
java
public class Folder extends FileSystemComponent {
private List<FileSystemComponent> children;
public Folder(String name) {
super(name);
this.children = new ArrayList<>();
}
@Override
public void display() {
System.out.println("folder: " + name);
for (FileSystemComponent component : children) {
component.display();
}
}
@Override
public void add(FileSystemComponent component) {
children.add(component);
}
@Override
public void remove(FileSystemComponent component) {
children.remove(component);
}
@Override
public FileSystemComponent getChild(int index) {
if (index >= 0 && index < children.size()) {
return children.get(index);
}
return null;
}
public int getChildCount() {
return children.size();
}
public List<FileSystemComponent> getChildren() {
return new ArrayList<>(children);
}
}
测试和输出:
java
public class Client {
static void main() {
System.out.println("===文件系统示例 ===\n");
// 创建文件
FileSystemComponent file1 = new File("document.txt", "txt");
FileSystemComponent file2 = new File("image.jpg", "jpg");
FileSystemComponent file3 = new File("data.pdf", "pdf");
FileSystemComponent file4 = new File("program.exe", "exe");
FileSystemComponent file5 = new File("config.ini", "ini");
// 创建文件夹
Folder root = new Folder("我的电脑");
Folder documents = new Folder("文档");
Folder images = new Folder("图片");
Folder system = new Folder("系统");
// 构建文件夹结构
documents.add(file1);
documents.add(file3);
images.add(file2);
system.add(file4);
system.add(file5);
root.add(documents);
root.add(images);
root.add(system);
// 显示文件系统结构
System.out.println("1. 完整的文件系统结构:");
root.display();
}
}
===文件系统示例 ===
完整的文件系统结构:
folder: 我的电脑
folder: 文档
document.txt(txt)
data.pdf(pdf)
folder: 图片
image.jpg(jpg)
folder: 系统
program.exe(exe)
config.ini(ini)
4. 优缺点
4.1 优点
符合开闭原则 :添加新类型的组件(如链接文件)无需修改现有代码
提高复用性 :可以复用叶子节点和组合节点,构建复杂的层次结构
增强可维护性 :简化客户端代码,客户端无需关心是单个对象还是组合对象
提高可读性 :通过树形结构清晰地表达了"部分-整体"的关系
支持递归组合:可以方便地构建任意复杂的对象结构
4.2 缺点
设计较为抽象 :增加了系统的抽象性和理解难度
类型检查问题 :在运行时可能需要类型检查来确定具体类型
性能考虑:对于深层次的树结构,递归操作可能有性能开销
5. 源码分析(JDK中的组合模式实现)
JDK中的map很典型地体现了组合模式思想,map接口中包含了一些列map增删方法,同时putAll方法,可以接受另一个Map
java
public static void main(String[] args) {
// 叶子
Map<String, Integer> map1 = new HashMap<>();
map1.put("A", 1);
map1.put("B", 2);
// 叶子
Map<String, Integer> map2 = new HashMap<>();
map2.put("C", 3);
// 组合
Map<String, Map<String, Integer>> nestedMap = new HashMap<>();
nestedMap.put("Nested", map1);
//
Map<String, Object> result = new HashMap<>();
result.putAll(map1);
result.putAll(map2);
// 添加嵌套Map
result.put("NestedMap", nestedMap);
System.out.println(result);
}
putAll的定义
java
public interface Map<K, V> {
// ... 其他方法
// putAll方法接受另一个Map(可以看作组合对象)
void putAll(Map<? extends K, ? extends V> m);
}
HashMap实现
java
public class HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Cloneable, Serializable {
@Override
public void putAll(Map<? extends K, ? extends V> m) {
// 这里的m可以是单个HashMap,也可以是嵌套的Map结构
// 客户端不需要知道m的具体结构,统一对待
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}
}
分析:
Map接口充当了抽象组件角色- 简单的
map实例可以看作是叶子节点 - 包含其他Map的Map可以看作是组合节点
putAll方法允许客户端统一处理单个Map和嵌套的Map结构- 客户端代码不需要关心Map内部的具体结构,只需通过统一接口操作
注意 :严格来说,JDK的Map实现不是标准的组合模式,因为它没有明确定义Component、Leaf、Composite的层次结构,但它体现了组合模式的核心思想------统一处理单个对象和组合对象
参考: