定义
组合模式最初只是用于解决树形结构的场景,更多的是处理对象组织结构之间的问题。而组合关系则是通过将不同对象封装起来完成一个统一功能
组合模式(composite pattern)的定义是:将对象组合成树形结构以表示整个部分的层次结构。组合模式可以让用户统一对待单个对象和对象的组合
结构图
案例演示
列出某一目录下所有的文件和文件夹
代码
/**
* @Description: 抽象类(文件夹+文件)
* @author: zhuoyue
* @since: 2024/05/13 15:32
*/
public abstract class Entry {
public abstract String getName();
public abstract int getSize();
/**
* 添加文件或文件夹
* @param entry
* @return
*/
public abstract Entry add(Entry entry);
/**
* 显示指定目录下的所有文件的信息
* @param entry
*/
public abstract void printList(String entry);
@Override
public String toString() {
return getName()+"("+getSize()+")";
}
}
public class File extends Entry{
private String name;
private int size;
public File(String name, int size) {
this.name = name;
this.size = size;
}
@Override
public String getName() {
return name;
}
@Override
public int getSize() {
return size;
}
@Override
public Entry add(Entry entry) {
return null;
}
@Override
public void printList(String entry) {
System.out.println(entry+"/"+this);
}
public void setSize(int size) {
this.size = size;
}
}
public class Directory extends Entry{
private String name;
private ArrayList<Entry> directory = new ArrayList<>();
public Directory(String name) {
this.name = name;
}
@Override
public String getName() {
return null;
}
@Override
public int getSize() {
int size = 0;
for (Entry entry: directory){
size += entry.getSize();
}
return size;
}
@Override
public Entry add(Entry entry) {
directory.add(entry);
return this;
}
@Override
public void printList(String prefix) {
System.out.println(prefix+"/"+this);
for (Entry entry: directory){
entry.printList(prefix+"/"+name);
}
}
}
public class Client {
public static void main(String[] args) {
/**
* 根节点
*/
Directory rootDir = new Directory("root");
/**
* 树枝节点
*/
Directory binDir = new Directory("bin");
binDir.add(new File("vi",10000));
binDir.add(new File("test",20000));
Directory tmpDir = new Directory("tmp");
Directory usrDir = new Directory("usr");
Directory mysqlDir = new Directory("mysql");
mysqlDir.add(new File("my.cnf",30));
mysqlDir.add(new File("test.db",25000));
usrDir.add(mysqlDir);
rootDir.add(mysqlDir);
rootDir.add(binDir);
rootDir.add(tmpDir);
rootDir.add(usrDir);
rootDir.printList("");
}
}
总结
组合模式分类
- 透明组合模式
在透明组合模式中,抽象根节点橘色中声明了所有用于管理成员对象的方法。
缺点是不够安全,因为叶子对象和容器对象本质上还是有区别的,叶子对象不可能有下一个层次的对象,即不可能包含成员对象,因此为其提供add()、remove()等方法是没有意义的,这在编译阶段不会出错,但在运行阶段如果调用者这些方法可能会出错,(可以抛出异常)
- 安全组合模式
在抽象构件角色中不声明任何用于管理成员对象的方法,而是在树枝节点类中声明并实现这些方法。安全组合模式的缺点是不够透明,因为叶子构件和容器构件具有不同的方法,且容器构件中那些用于管理成员对象的方法没有在抽象构件中定义,因此客户端不能完全针对抽象编程,必须有区别的针对叶子构件和容器构件
优点
- 组件模式可以清楚的定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。
- 客户端可以一致的使用一个组合结构或其中单个对象,不必关心处理的结构是单个对象还是整个组织结构,简化了客户端代码。
- 在组合模式中增加新的树枝节点或叶子节点都很方便,无须对现有类库进行任何修改,符合“开闭原则”
- 组合模式为树形结构对象实现提供了一种灵活的解决方案,通过叶子节点和树枝节点的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单
缺点
- 使用组合模式的前提在于你的业务场景是否能够表示成树形结构。所以组合模式的应用场景也比较局限,所以它并不是一种很常用的设计模式
应用场景
- 处理一个树形结构,比如,公司人员组织架构、订单信息等
- 跨越多个层次结构聚合数据,比如,统计文件夹下的文件总数
- 统一处理一个结构中的多个对象,比如遍历文件夹下的所有XML类型文件内容