组合模式
介绍
我们的计算机都拥有文件系统,文件夹里面既可以放入文件,也可以放入子文件夹。在子文件夹中,一样地既可以放入文件,也可以放入子文件夹。文件系统是一种容器结构、递归结构。
组合模式就是用于创造出这样的结构的模式。组合模式又叫部分整体模式,用于把一组相似的对象当做一个单一的对象,它将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
实现
在组合模式中,一共有以下几个角色:
- Leaf(树叶)
表示“内容”的角色。该角色不能放入其他的对象。 - Composite(复合物)
表示容器的角色。可以在其中放入Leaf角色和Composite角色。 - Component
使Leaf角色和Composite角色具有一致性的角色。Component角色是Leaf角色和Composite角色的父类。 Client
组合模式的使用者。
以文件系统的显示为例,我们写一段示例程序。Entry类(扮演Component角色)
public abstract class Entry { private String name; public Entry(String name) { this.name = name; } public String getName() { return this.name; } public abstract Entry add(Entry entry); public abstract boolean delete(Entry entry); public abstract LinkedList getChild(); }
Directory类(扮演Composite角色)
public class Directory extends Entry { private LinkedList<Entry> children = new LinkedList<Entry>(); public Directory(String name) { super(name); } @Override public Entry add(Entry entry) { this.children.add(entry); return this; } @Override public boolean delete(Entry entry) { return this.children.remove(entry); } @Override public LinkedList<Entry> getChild() { return this.children; } }
File类(扮演Leaf角色)
public class File extends Entry { public File(String name) { super(name); } @Override public Entry add(Entry entry) { throw new UnsupportedOperationException(); } @Override public boolean delete(Entry entry) { throw new UnsupportedOperationException(); } @Override public LinkedList getChild() { throw new UnsupportedOperationException(); } }
Client类(扮演Client角色)
public class Client { public static void main(String[] args) { Entry rootDir = new Directory("root"); Entry homeDir = new Directory("home"); Entry tmpDir = new Directory("tmp"); Entry usrDir = new Directory("usr"); rootDir.add(tmpDir); rootDir.add(homeDir); rootDir.add(usrDir); usrDir.add(new File("bin")); usrDir.add(new File("lib")); showDirectory(rootDir, ""); } public static void showDirectory(Entry entry, String prefix) { if (entry instanceof Directory) { prefix = prefix + java.io.File.separator + entry.getName(); Iterator it = entry.getChild().iterator(); while (it.hasNext()) { Entry child = it.next(); if (child instanceof Directory) { showDirectory(child, prefix); } else { System.out.println(prefix + java.io.File.separator + child.getName()); } } if (entry.getChild().size() == 0) { System.out.println(prefix); } } else { System.out.println(prefix + java.io.File.separator + entry.getName()); } } }
输出结果
/root/tmp /root/home /root/usr/bin /root/usr/lib
Tips
上面的实现方式为透明模式的组合模式,即add()、remove()、getChild()方法在父类申明,不管Directory类还是File类都可见,如果使用不当调用了File类的实现方法,程序会抛出异常。
因此,组合模式还有另外一种实现方式→安全模式,即将add()、remove()、getChild()方法单独放在Directory类中,File类无法调用。类图如下:
组合模式总结
- 优点
1)可以自由增加各个节点,扩展性好,方便后期维护
2)高层模块调用简单,结构中的所有节点都是Component,局部和整体对调用者来说没有任何区别,也就是说,高层模块不必关心自己处理的是单个对象还是整个组合结构,简化了高层模块的代码。 - 缺点
1)透明模式的方式,对使用者要求较高,使用不当易抛出异常
2)安全模式的方式,申明Composite和Leaf类的时候,必须使用实现类申明,与依赖倒置原则冲突。