定义
- 将对象组合成树形结构以表示 "部分-整体" 的层次结构。
- 组合模式使客户端对对单个对象和组合对象保持一致的方式处理。
- 组合模式用来表示部分与整体的层次结构(类似于树结构),而且也可以使用户对单个对象(叶子节点)以及组合对象(非叶子节点)的使用具有一致性,一致性的意思就是说,这些对象都拥有相同的接口。
- 如文件系统,书的目录,网站的菜单都可以使用组合模式。
简单来说,组合模式能让我们能用树形的方式创建对象结构,树中包含了组合对象(如文件夹)以及单一对象(文件夹下的文件),我们能使用相同的操作应用在组合和单一对象上,可以忽略组合和单一对象的差异性。
类型
结构型
适用场景
- 希望客户端可以忽略组合对象和单一对象时
- 处理一个树形结构时
优点
- 可以清楚的定义分层次的复杂对象,表示对象的全部或部分整体;
- 让客户端忽略了层次的差异,方便对整个层次结构进行控制;
- 简化客户端代码;
- 符合开闭原则;
缺点
- 限制类型时会较为复杂
- 使设计便得抽象
UML类图
组合模式又分为透明模式和安全模式。
透明模式是把组合使用的方法放到抽象类中,不管叶子对象还是树枝对象都有相同的结构,这样做的好处就是叶子节点和树枝节点对于外界没有区别,它们具备完全一致的行为接口。
但同样叶子节点的有些方法可能只是空实现了接口,所以实现它是没有意义的。UML结构图如下:
在这里,leaf是单一对象,composite就是组合对象,通过组合模式,它们都实现了相同的方法,虽然在leaf中add和remove方法是没有意义的。
安全模式是把树枝节点和树叶节点彻底分开,树枝节点单独拥有用来组合的方法,这种方法比较安全。但由于不够透明,所以树叶节点和树枝节点将不具有相同的接口或方法,客户端的调用需要做相应的判断,带来了不便。UML结构图如下:
代码Demo
业务场景:文件夹,文件夹下又多个文件
- FileComponent
public interface FileComponent {
void add(FileComponent component);
void remove(FileComponent component);
void print();
}
- File(Leaf)
public class File implements FileComponent {
private String name;
public File(String name) {
this.name = name;
}
@Override
public void add(FileComponent component) {}
@Override
public void remove(FileComponent component) {}
@Override
public void print() {
System.out.println(name);
}
}
- Folder(Composite)
public class Folder implements FileComponent {
private ArrayList<FileComponent> arrayList = new ArrayList<>();
@Override
public void add(FileComponent component) {
arrayList.add(component);
}
@Override
public void remove(FileComponent component) {
arrayList.remove(component);
}
@Override
public void print() {
System.out.println("文件夹");
for (FileComponent fileComponent:arrayList){
System.out.print(" ");
fileComponent.print();
}
}
}
- 应用层
public class Test {
public static void main(String[] args) {
FileComponent folder = new Folder();
folder.add(new File("文件1"));
folder.add(new File("文件2"));
folder.add(new File("文件3"));
folder.print();
}
}
源码中的组合模式
ArrayList类中的addAll方法就采用了组合模式
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
public interface List<E> extends Collection<E>
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
ArrayList实现了List接口,而List接口继承了Collection接口,所以在这里正式组合模式的应用。
HashMap类中的putAll也和上面的方法相同
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
public void putAll(Map<? extends K, ? extends V> m) {
putMapEntries(m, true);
}