组合模式
将对象组合成树形结构以表示“部分-整体”的层次结构。创建一个包含自己对象组的类,提供修改对象组的方式,让用户对单个对象和组合对象的访问具有一致性
UML:

Composite对象结构:

组合模式的主要角色:
Component(抽象组件):为组合中所有对象提供一个接口,适当情况下实现所有类共有的接口默认行为
Composite(组合节点):实现了Component的所有操作,并且持有子节点对象
Leaf(叶子节点):叶子节点没有子节点,实现了Component中的某些操作。
Cast:以文件系统为例,输出文件树
AbstractFile(Component):
1 public abstract class AbstractFile { 2 3 protected String name; 4 5 Map map = new HashMap(); 6 7 protected void add(AbstractFile file) { 8 throw new UnsupportedOperationException(); 9 } 10 11 protected void remove(AbstractFile file) { 12 throw new UnsupportedOperationException(); 13 } 14 15 public AbstractFile(String name) { 16 this.name = name; 17 } 18 19 // 子类实现抽象方法 20 protected abstract void show(int depth); 21 22 public String getName() { 23 return name; 24 } 25 26 public void setName(String name) { 27 this.name = name; 28 } 29 }
Directory(Composite):
1 public class Directory extends AbstractFile { 2 3 List<AbstractFile> files = new ArrayList<>(); 4 5 public Directory(String name) { 6 super(name); 7 } 8 9 @Override 10 protected void add(AbstractFile file) { 11 files.add(file); 12 } 13 14 @Override 15 protected void remove(AbstractFile file) { 16 files.remove(file); 17 } 18 19 @Override 20 protected void show(int depth) { 21 for (int i = 0; i < depth; i++) { 22 System.out.print(" "); 23 } 24 System.out.println(getName()); 25 for (AbstractFile f : files) { 26 f.show(++depth); 27 --depth; 28 } 29 } 30 }
MyFile(Leaf):
public class MyFile extends AbstractFile { public MyFile(String name) { super(name); } @Override protected void show(int depth) { for(int i = 0; i < depth; i++) { System.out.print(" "); } System.out.println(getName()); } }
Client:
1 public class Client { 2 public static void main(String[] args) { 3 AbstractFile a = new Directory("a"); 4 AbstractFile b = new Directory("b"); 5 AbstractFile c = new Directory("c"); 6 AbstractFile d = new Directory("d"); 7 8 AbstractFile t1 = new MyFile("1.txt"); 9 AbstractFile t2 = new MyFile("2.exe"); 10 AbstractFile t3 = new MyFile("3.docx"); 11 AbstractFile t4 = new MyFile("4.jpg"); 12 AbstractFile t5 = new MyFile("5.bat"); 13 14 a.add(b); a.add(c); a.add(t1); 15 b.add(t2); c.add(t3); 16 c.add(t4); c.add(d); 17 d.add(t5); 18 19 a.show(0); 20 } 21 }
运行结果:

透明组合模式:
透明组合模式也是组合模式的标准形式,上述UML、Cast等均为透明模式
抽象构件 Component 声明了所有用于管理成员对象的方法如包括add()、remove()以及getChild()等方法,确保所有的构件类都有相同的接口。客户端可以相同地对待所有的对象。
缺点:不够安全,因为 Leaf 和 composite 本质上是有区别的。Leaf 没有子节点,不可能包含成员对象,因此为其提供add()、remove()以及getChild()等方法是没有意义的,违背了接口隔离原则,虽然在编译阶段不会出错,但在运行阶段如果调用这些方法可能会出错(如果没有提供相应的错误处理代码)。
HashMap使用了透明组合模式:
Map是抽象构件Component,putAll方法传入Map对象:

HashMap是composite,而叶子节点是HashMap的静态内部类

HashMap使用Node<K,V>数组存储子节点

安全组合模式:
安全组合模式中,抽象构件Component中没有声明任何用于管理成员对象的方法,而是在Composite中声明并实现这些方法。Leaf 没有管理成员对象的方法,客户端也不可能调用到这些方法,因此是安全的
UML2:

缺点:不够透明,因为 Leaf 和 composite 具有不同的方法,且 composite 中那些用于管理成员对象的方法没有在抽象构件component中定义,因此客户端不能完全针对抽象编程,必须有区别地对待 leaf 和 composite
Android中的ViewGroup就是使用安全组合模式,UML如图:

总结:
既然有两种实现方式,肯定有不同的使用场景
如果系统中大多数层次具有相同的行为,可以使用透明模式(代价是为少数叶子结点引入不需要的方法)
相反若各层次行为差异较大,可以使用安全模式

浙公网安备 33010602011771号