组合模式
将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。
类结构图
Component
组合中的对象声明接口,在适当情况下,实现所有类共有的接口默认行为。声明一个接口用于访问和管理 Component 的子部件。
Composite
定义节点的行为,用来存储子部件,在 Component 接口中实现与子部件有关的操作,比如增加 add 和删除 remove。
Leaf
在组合中表示叶节点对象,叶节点没有子节点。
树结构的例子有很多,文件、公司、菜单的结构。下面我们就用文件的例子来看看。
代码实现
public interface IFile {
void delete();
String getName();
void newFile(String name);
void delFile(String name);
IFile getFile(int index);
}
public class Folder implements IFile {
private String name;
private IFile folder;
private List<IFile> files;
public Folder(String name) {
this(name, null);
}
public Folder(String name, IFile folder) {
this.name = name;
this.folder = folder;
files = new ArrayList<>();
}
@Override
public String getName() {
return this.name;
}
@Override
public void delete() {
Iterator<IFile> iterator = files.iterator();
while (iterator.hasNext()) {
iterator.remove();
}
System.out.println(">>>>>>>> 删除子文件结束 <<<<<<<<<<");
if (null != this.folder) {
this.folder.delFile(this.name);
}
System.out.println(">>>>>>>> 删除 " + this.name + " 结束 <<<<<<<<<<");
}
@Override
public void newFile(String name) {
if (name.contains(".")) {
files.add(new File(name, this));
} else {
files.add(new Folder(name, this));
}
}
@Override
public void delFile(String name) {
Iterator<IFile> iterator = files.iterator();
while (iterator.hasNext()) {
IFile file = iterator.next();
if (file.getName().equals(name)) {
iterator.remove();
}
}
}
@Override
public IFile getFile(int index) {
return this.files.get(index);
}
}
public class File implements IFile {
private String name;
private IFile folder;
public File(String name, IFile folder) {
this.name = name;
this.folder = folder;
}
@Override
public String getName() {
return this.name;
}
@Override
public void delete() {
this.folder.delFile(this.name);
System.out.println(">>>>>>>>>> 删除 " + this.name + " <<<<<<<<<<");
}
@Override
public void newFile(String name) {
System.out.println(">>>>>>>>>> 不支持创建新文件 <<<<<<<<<<");
}
@Override
public void delFile(String name) {
System.out.println(">>>>>>>>>> 不支持删除子文件 <<<<<<<<<<");
}
@Override
public IFile getFile(int index) {
System.out.println(">>>>>>>>>> 不支持获取子文件 <<<<<<<<<<");
return null;
}
}
客户端示例
public class Client {
public static void main(String[] args) {
IFile root = new Folder("root 文件");
root.newFile("1 文件");
root.newFile("2 文件");
root.newFile("3 文件");
IFile c = root.getFile(0);
c.newFile("Program Files");
IFile program = c.getFile(0);
program.newFile("怎么学习设计模式.pdf");
IFile d = root.getFile(1);
d.newFile("学习学籍");
IFile pdfs = d.getFile(0);
pdfs.newFile("agile java.pdf");
pdfs.newFile("Effective Java 中文第二版.pdf");
pdfs.newFile("Java8实战.pdf");
showFiles(root);
}
private static final void showFiles(IFile iFile) {
showFiles(null, iFile);
}
private static final void showFiles(String prefix, IFile iFile) {
if (null == prefix) {
prefix = "";
}
System.out.println(prefix + iFile.getName());
if (iFile instanceof Folder) {
for (int i = 0; ; i++) {
try {
if (iFile.getFile(i) != null) {
showFiles(prefix + "--", iFile.getFile(i));
}
} catch (Exception e) {
break;
}
}
}
}
}
运行结果
root 文件
--1 文件
----Program Files
------怎么学习设计模式.pdf
--2 文件
----学习学籍
------agile java.pdf
------Effective Java 中文第二版.pdf
------Java8实战.pdf
--3 文件
Process finished with exit code 0
使用场景
当发现需求中是体现部分与整体层级的结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用结构中的所有对象时,就应该考虑使用组合模式了。
优点
- 组合模式使得客户端代码可以一致地处理对象和对象容器,无需关系处理的单个对象,还是组合的对象容器。
- 将”客户代码与复杂的对象容器结构“解耦。
- 可以更容易地往组合对象中加入新的构件。
缺点
使得设计更加复杂。客户端需要花更多时间理清类之间的层次关系。