组合模式是一种结构型设计模式,用于将对象组合成树形结构以表示“部分-整体”的层次结构。它让客户端可以统一对待单个对象和组合对象。
核心思想
- 统一接口: 定义一个接口,所有组件(无论是叶子还是组合)都实现这个接口。
- 递归组合: 组合对象可以包含多个子对象,这些子对象也可以是组合对象或叶子对象。
组件类型
- 叶子节点: 最小的基本对象,没有子对象。
- 组合节点: 容器对象,可以包含叶子或其他组合。
工作原理
- 定义组件接口: 包含所有子类需要实现的方法。
- 实现叶子类: 实现组件接口的方法,执行具体操作。
- 实现组合类: 包含子组件的列表,实现接口方法,并在这些方法中递归调用子组件的方法。
适用场景
- 希望客户端能以统一的方式处理单个对象和组合对象。
- 需要表示对象的部分-整体层次结构。
优点
- 简化客户端代码,不再需要关心处理的是单个对象还是组合对象。
- 更容易添加新类型的组件。
缺点
- 可能使设计更加复杂。
- 不容易限制组合中的组件类型。
示例
文件系统中,文件和文件夹就是组合模式的典型例子。文件夹可以包含文件或其他文件夹,客户端可以用相同的方式处理文件和文件夹。
应用场景
- 系统的文件处理系统
- UI界面的Menu item
- 公司人员职级划分
- ...
What is Composite Pattern
Composite Pattern(组合模式)也叫叉数、对象树、Object Tree、...。它的思想很类似自然界的树状结构。组合模式也是用于描述具有层次结构的数据。
树状结构很有意思的地方在于,每个树枝都是一样的,甚至树叶和树枝都是一样的,区别只是在于树叶下面没有树叶了。
组合模式就是借鉴了这个思想,它允许将对象组合成树状结构,并以统一的方式处理对象及对象组合。组合模式使得客户端可以一致地对待单个对象和对象组合,无需区分它们的差异。
关键要素
- leaf and branch interface
- leaf
- branch
- Client
Example —— 读取文件夹和文件
假设我们要设计一个文件系统的结构,包括文件和文件夹。文件夹可以包含文件和其他文件夹,而文件没有子组件。我们可以使用组合模式来实现这个文件系统结构。
leaf and branch interface
public interface FileSystemComponent {
void showInfo();
}
leaf and folder
public class File implements FileSystemComponent {
private String name;
public File(String name) {
this.name = name;
}
public void showInfo() {
System.out.println("File: " + name);
}
}
public class Folder implements FileSystemComponent {
private String name;
private List<FileSystemComponent> components;
public Folder(String name) {
this.name = name;
this.components = new ArrayList<>();
}
public void addComponent(FileSystemComponent component) {
components.add(component);
}
public void removeComponent(FileSystemComponent component) {
components.remove(component);
}
public void showInfo() {
System.out.println("Folder: " + name);
for (FileSystemComponent component : components) {
component.showInfo();
}
}
}
Client
public class Client {
public static void main(String[] args) {
// 创建文件和文件夹
FileSystemComponent file1 = new File("file1.txt");
FileSystemComponent file2 = new File("file2.doc");
Folder folder1 = new Folder("Folder 1");
Folder folder2 = new Folder("Folder 2");
// 组合文件和文件夹
folder1.addComponent(file1);
folder1.addComponent(file2);
folder1.addComponent(folder2);
// 显示文件系统结构
folder1.showInfo();
}
}
out :
Folder: Folder 1
File: file1.txt
File: file2.doc
Folder: Folder 2
通过组合模式,我们可以将文件和文件夹组织成树状结构,使用统一的方式处理它们。
客户端可以递归地遍历文件系统的组件,无需关心是单个文件还是文件夹。这样,组合模式提供了一种灵活且可扩展的方式来处理对象的组合关系。
Example —— 统计城市人口数
这个案例并不是那么恰当!
interface
public interface Counter {
int count();
}
leaf
public class Leaf implements Counter {
private int sum = 0;
public Leaf(int sum) {
this.sum = sum;
}
@Override
public int count() {
return sum;
}
}
Branch
public class Branch implements Counter {
private List<Counter> counterList = new ArrayList<>();
public void add(Counter counter) {
counterList.add(counter);
}
public void delete(Counter counter) {
counterList.remove(counter);
}
public List<Counter> getChild() {
return counterList;
}
@Override
public int count() {
int sum = 0;
for (Counter counter : counterList) {
sum += counter.count();
}
return sum;
}
}
Client
/**
* 需求是做一个统计全国人口数量的功能
* 全国根据地区划分就是一个树状结构
* ------------------------------
* 国家
* 城市1
* 区1
* 区2
* 城市2
* 区1
* 区2
* ------------------------------
*/
public static void main(String[] args) {
System.out.println("开始统计人数");
Branch city = new Branch(); // 模拟市
Leaf beijing = new Leaf(100);
Leaf shanghai = new Leaf(200);
city.add(beijing);
city.add(shanghai);
Branch district = new Branch(); // 模拟区
Leaf dongcheng = new Leaf(300);
Leaf xicheng = new Leaf(400);
district.add(dongcheng);
district.add(xicheng);
city.add(district); // 区加入城市
System.out.println(city.count()); //1000
}