组合模式

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

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 }
View Code

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 }
View Code

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());
    }
}
View Code

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 }
View Code

运行结果:

透明组合模式:

  透明组合模式也是组合模式的标准形式,上述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如图:

总结:

既然有两种实现方式,肯定有不同的使用场景

如果系统中大多数层次具有相同的行为,可以使用透明模式(代价是为少数叶子结点引入不需要的方法)

相反若各层次行为差异较大,可以使用安全模式

 

posted @ 2020-08-18 21:09  whyha  阅读(178)  评论(0)    收藏  举报