组合模式
组合模式也称为部分-整体模式,结构型设计模式之一。
1.定义
将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
2.使用场景
(1)表示对象的部分-整体层次结构时。
(2)从一个整体中能够独立出部分模块或功能的场景。
3.UML类图
(1)Component:抽象根节点,为组合中的对象声明接口。在适当的情况下,实现所有类共有接口的缺省行为。声明一个接口用于访问和管理Component的子节点。可在递归结构中定义一个接口,用于访问一个父节点,并在合适的情况下实现它。
(2)Composite:定义有子节点的那些枝干节点行为,存储子节点,在Component接口中实现与子节点有关的操作。
(3)Leaf:在组合中表示叶子节点对象,叶子节点没有子节点,在组合中定义节点对象的行为。
(4)Client:通过Component接口操纵组合节点的对象。
如图这种将组合所使用的方法全部定义在抽象类的方式称为透明的组合模式,如果将Component中的Add、Remove、GetChild去除,只在Composite中单独添加,这种方式称为安全的组合模式。然而后者违背了依赖倒置原则。
4.简单实现
以文件和文件夹这样的文件系统为例
文件和文件夹的抽象类:(Component)
1 2 3 4 5 | <code class="hljs java">public abstract class Dir { /** * 声明一个List成员变量来储存文件夹下的所有元素 */ protected List</code> |
dirs = new ArrayList
(); private String name; //当前文件或文件夹名 public Dir(String name) { this.name = name; } /** * 添加一个文件或文件夹 * * @param dir 文件或文件夹 */ public abstract void addDir(Dir dir); /** * 移除一个文件或文件夹 * * @param dir 文件或文件夹 */ public abstract void rmDir(Dir dir); /** * 清空文件夹下所有元素 */ public abstract void clear(); /** * 输出文件夹目录结构 */ public abstract void print(); /** * 获取文件夹下所有的文件或文件夹 * * @return 文件夹下所有的文件或文件夹 */ public abstract List
getFiles(); /** * 获取文件或文件夹名 * * @return 文件或文件夹名 */ public String getName(){ return name; } }
表示文件夹的类:(Composite)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <code class="hljs java">public class Folder extends Dir{
public Folder(String name) { super(name); }
@Override public void addDir(Dir dir) { dirs.add(dir); }
@Override public void rmDir(Dir dir) { dirs.remove(dir); }
@Override public void clear() { dirs.clear(); }
@Override public void print() { System.out.print(getName() + "("); Iterator</code> |
iter = dirs.iterator(); while (iter.hasNext()) { Dir dir = iter.next(); dir.print(); if(iter.hasNext()){ System.out.print(", "); } } System.out.print(")"); } @Override public List
getFiles() { return dirs; } }
表示文件夹的类:(Leaf)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | <code class="hljs java">public class File extends Dir{
public File(String name) { super(name); }
@Override public void addDir(Dir dir) { throw new UnsupportedOperationException("文件对象不支持该操作!"); }
@Override public void rmDir(Dir dir) { throw new UnsupportedOperationException("文件对象不支持该操作!"); }
@Override public void clear() { throw new UnsupportedOperationException("文件对象不支持该操作!"); }
@Override public void print() { System.out.print(getName()); }
@Override public List</code> |
getFiles() { throw new UnsupportedOperationException("文件对象不支持该操作!"); } }
客户类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <code class="hljs cs">public class Client { public static void main(String[] args) { //构造一个目录对象表示C盘根目录 Dir diskC = new Folder("C");
//C盘根目录下有一个文件Log.txt diskC.addDir(new File("Log.txt"));
//C盘根目录下有三个目录Windows、PerfLogs、Program File Dir dirWin = new Folder("Windows");
//Windows目录下有文件explorer.exe dirWin.addDir(new File("explorer.exe")); diskC.addDir(dirWin);
//PerfLogs目录 Dir dirPer = new Folder("PerfLogs");
//PerfLogs目录下有文件null.txt dirPer.addDir(new File("null.txt")); diskC.addDir(dirPer);
//Program File目录 Dir dirPro = new Folder("Program File");
//Program File目录下有文件ftp.txt dirPro.addDir(new File("ftp.txt")); diskC.addDir(dirPro);
//打印出文件结构 diskC.print(); } } </code> |
结果:
1 | <code class="hljs avrasm">C(Log.txt, Windows(explorer.exe), PerfLogs(null.txt), Program File(ftp.txt))</code> |
源码中的模式实现">5.Android源码中的模式实现
1.View和ViewGroup的嵌套组合
View和ViewGroup的结构很像上面的UML类图,不过View的视图层级使用的是安全的组合模式。ViewGroup有对View的addView、removeView、getChildAt等方法,想必大家也很熟悉。
6.总结
1.优点
(1)组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,他让高层模块忽略了层次的差异,方便对整个层次结构进行控制。
(2)简化了高层模块的代码。
(3)在组合模式中增加新的枝干构件和叶子构件都很方便,无须对现有类库进行修改,符合“开闭原则”。
(4)对树形结构的控制变得简单。
2.缺点
组合模式不容易限制组合中的构件。因为大多数情况下,它们都来自于相同的抽象层,此时,必须进行类型检查来实现,这个实现过程较为复杂。