组合模式(Composite Pattern)
- 组合模式概述
定义:组合多个对象形成树形结构以表示具有部分-整体关系的层次结构。组合模式让客户端可以统一对待单个对象和组合对象。又被成为“部分-整体”(part-whole)模式,属于对象结构性模式
定义什么的,最枯燥了。简单的来说,就如我们鼠标右击新建文件夹一样,在一个磁盘里,我们可以新建一个文件夹a,也可以新建某个类型的文件a,a和a都在同一目录下,当然,我们也可以点击进入文件夹a,在a中新建新的文件夹b,也可以在a中新建文件b。只要是文件夹,就可以在里面继续新建文件夹和文件,而文件则只能用来放数据,不能在新建了。
当然,也可以通过win+r调出运行,输入cmd(commmand),进入你想查看文件的磁盘,输入tree命令,就可以结构性的查看所有文件。在上述图中可以看出文件结构就如同一棵树,有文件夹,有文件,我们把文件成为叶子(laef),因为文件不能再展开了,就如树形结构中叶子是最底层了;把文件夹称为容器(container),因为文件夹里既可以文件夹,又可以放文件。当想要查看一个磁盘的所有文件时,可以使用dfs(深度优先搜索)即递归的对每个文件夹进行同样的搜索,一条路走到底,当访问到叶子结点时,再退回去访问别的结点。
- 组合模式结构和实现
- component(抽象构件):它可以是接口或抽象类,为叶子构件和容器构件对象声明接口。定义了访问管理它的子构件的方法,就是对一个子构件该有的操作都应该在这声明,如创建子构件,删除子构件,打开子构件,重命名子构件,复制子构件.......
- leaf(叶子构件):代表一个文件,实现在component中的行为,即对文件的操作,可以是读文件,写文件,删除文件,复制文件......
- composite(容器构件):代表一个文件夹,它提供一个集合放子节点,可以是文件夹,也可以是文件,所以它有一个聚合箭头指向component,代表可以递归的创建构件
1 abstract class component 2 { 3 public abstract void add(component c); 4 public abstract void remove(component c); 5 public abstract component getchild(int i); 6 public abstract void operation(); 7 } 8 class leaf : component 9 { 10 public override void add(component c) 11 { 12 //throw new notimplementedexception();//这里是报错,叶子文件没有此方法的 13 } 14 15 public override void remove(component c) 16 { 17 //throw new notimplementedexception();//同上 18 } 19 20 public override component getchild(int i ) 21 { 22 //throw new notimplementedexception();//同上 23 return null; 24 } 25 26 public override void operation() 27 { 28 //对文件的可执行方法 29 } 30 } 31 class composite : component 32 { 33 private ilist<component> list = new list<component>();//这里通链表容器存放文件夹 34 public override void add(component c) 35 { 36 list.add(c); 37 } 38 39 public override void remoce(component c) 40 { 41 list.remove(c); 42 } 43 44 public override component getchild(int i ) 45 { 46 return (component)list(c); 47 } 48 49 public override void operation() 50 { 51 //递归实现容器中的对文件的方法 52 foreach(component child in list) 53 { 54 (component)child.operation(); 55 } 56 } 57 }
- 组合模式的应用
教育机构的oa系统要给各办公室下发公文(issuedbytheofficedoucument),采用组合模式模拟实现
分析:从图中可以看出,北京总部,湖南分校,长沙叫教学点,湘潭教学点这几个有子构件,所以它们为文件容器,而教务办公室,行政办公室即为叶子,当然,每个叶子是不同的,要区别开。因此我们定义一个抽象机构类(abstractinstitutions),其中声明add,remove,getchild和issuedbytheofficedoucument方法。
1 abstract class abstractinstitutions//component 2 { 3 public abstract void add(abstractinstitutions institutions); 4 public abstract void remove(abstractinstitutions institutions); 5 public abstract abstractinstitutions getchild(int i); 6 public abstract void issuedbytheofficedoucument(); 7 }
教务办公室,有自己的名字,即哪一个教务办公室,它只能收到下发的公文
1 class academicaffairsoffice : abstractinstitutions//leaf 教务办公室 2 { 3 private string name; 4 public academicaffairsoffice(string name) 5 { 6 this.name = name; 7 } 8 public override void add(abstractinstitutions institutions) 9 { 10 console.writeline("can't realize this method!!!"); 11 } 12 13 public override void remove(abstractinstitutions institutions) 14 { 15 console.writeline("can't realize this method!!!"); 16 } 17 18 public override abstractinstitutions getchild(int i) 19 { 20 console.writeline("can't realize this method!!!"); 21 return null; 22 } 23 24 public override void issuedbytheofficedoucument() 25 { 26 console.writeline("issued by the office doucument to the {0}",name); 27 } 28 }
行政办公室和教务办公室类似的实现
1 class administrationoffice : abstractinstitutions//leaf 行政办公室 2 { 3 private string name; 4 public administrationoffice(string name) 5 { 6 this.name = name; 7 } 8 9 public override void add(abstractinstitutions institutions) 10 { 11 console.writeline("can't realize this method!!!"); 12 } 13 14 public override void remove(abstractinstitutions institutions) 15 { 16 console.writeline("can't realize this method!!!"); 17 } 18 19 public override abstractinstitutions getchild(int i) 20 { 21 console.writeline("can't realize this method!!!"); 22 return null; 23 } 24 25 public override void issuedbytheofficedoucument() 26 { 27 console.writeline("issued by the office doucument to the {0}", name); 28 } 29 }
容器类中实现抽象构件中的方法,并且要向下属机构下发公文
1 class teacharea : abstractinstitutions//composite //教学点 2 { 3 private string name; 4 private ilist<abstractinstitutions> arealist = new list<abstractinstitutions>(); 5 6 public teacharea(string name) 7 { 8 this.name = name; 9 } 10 11 public override void add(abstractinstitutions institutions) 12 { 13 arealist.add(institutions); 14 } 15 16 public override void remove(abstractinstitutions institutions) 17 { 18 arealist.remove(institutions); 19 } 20 21 public override abstractinstitutions getchild(int i) 22 { 23 return (abstractinstitutions)arealist[i]; 24 } 25 26 public override void issuedbytheofficedoucument() 27 { 28 console.writeline("issue official documents to the {0}", name); 29 foreach (object obj in arealist) 30 { 31 ((abstractinstitutions)obj).issuedbytheofficedoucument(); 32 } 33 } 34 }
program中,要将所有的机构创建出来,并一一add到对应的容器中,最后有由北京总部向下发送公文
1 class program 2 { 3 static void main(string[] args) 4 { 5 abstractinstitutions beijingheadquarters; 6 beijingheadquarters = new teacharea("beijingheadquarters"); 7 8 abstractinstitutions beijingacademicaffairsoffice; 9 beijingacademicaffairsoffice = new academicaffairsoffice("beijingacademicaffairsoffice"); 10 administrationoffice beijingadministrationoffice; 11 beijingadministrationoffice = new administrationoffice("beijingadministrationoffice"); 12 13 abstractinstitutions hunanpartition; 14 hunanpartition = new teacharea("hunanpartition"); 15 16 abstractinstitutions hunanchangshaarea; 17 hunanchangshaarea = new teacharea("hunanchangshaarea"); 18 19 abstractinstitutions hunanxiangtanarea; 20 hunanxiangtanarea = new teacharea("hunanxiangtanarea"); 21 22 abstractinstitutions hunanacademicaffairoffice; 23 hunanacademicaffairoffice = new academicaffairsoffice("hunanacademicaffairoffice"); 24 abstractinstitutions hunanadministrationoffice; 25 hunanadministrationoffice = new administrationoffice("hunanadministrationoffice"); 26 27 console.writeline(); 28 abstractinstitutions changshaacademicaffairoffice; 29 changshaacademicaffairoffice = new academicaffairsoffice("changshaacademicaffairoffice"); 30 31 abstractinstitutions changshaadministrationoffice; 32 changshaadministrationoffice = new administrationoffice("changshaadministrationoffice"); 33 abstractinstitutions xiangtanacademicaffairoffice; 34 xiangtanacademicaffairoffice = new academicaffairsoffice("xiangtanacademicaffairoffice"); 35 36 abstractinstitutions xiangtanadministrationoffice; 37 xiangtanadministrationoffice = new administrationoffice("xiangtanadministrationoffice"); 38 39 hunanchangshaarea.add(changshaacademicaffairoffice); 40 hunanchangshaarea.add(changshaadministrationoffice); 41 42 hunanxiangtanarea.add(xiangtanacademicaffairoffice); 43 hunanxiangtanarea.add(xiangtanadministrationoffice); 44 45 hunanpartition.add(hunanchangshaarea); 46 hunanpartition.add(hunanxiangtanarea); 47 hunanpartition.add(hunanacademicaffairoffice); 48 hunanpartition.add(hunanadministrationoffice); 49 50 beijingheadquarters.add(beijingacademicaffairsoffice); 51 beijingheadquarters.add(beijingadministrationoffice); 52 beijingheadquarters.add(hunanpartition); 53 beijingheadquarters.issuedbytheofficedoucument(); 54 } 55 }
运行结果:
- 透明组合模式和安全组合模式
- 透明组合模式
在component中声明所有的方法,这样满足了一致性的原则,即对叶子和容器对象都是一样的处理,不需用再去判断,这个对象是叶子还是容器啊,如果是容器,可以实现哪些方法,如果是叶子,又只能实现哪些方法。这些判断都不需要。但是,由于叶子也继承了component的方法,因此不安全,叶子对象不能调用add,remove,getchild的方法,如果运行时调用,会出错,因此要提供错误处理代码(如上述例子中的( console.writeline("can't realize this method!!!");
- 安全组合模式
component中没有声明任何方法,而是在composite中声明并实现,这样做不会因为leaf调用错误的方法而报错,但缺点是不够透明,即要区别的对待叶子构件和容器构件,但是日常使用是很多的,毕竟安全的往往好很多
上面的例子使用了透明组合模式。
- 组合模式的优缺点和适用环境
- 组合模式的优点:
- 可以清楚的定义分层次的复杂对象,表示对象的全部会部分层次,让客户忽略层次的差异,方便控制
- 客户端可以一致性的使用一个组合结构或其中单个对象,不必关心处理的是单个对象(叶子文件)还是组合结构(文件夹容器),简化了客户端代码
- 增加新的容器构件和叶子构件很方便
- 为树型结构的面向对象实现了一种灵活的解决方案
- 组合模式的缺点:
- 在增加新的构件时很难对容器中的构件类型进行控制。如果我希望文件夹里只能放文件时,则需要复杂的实现过程来实现
- 组合模式的适用环境
- 在具有整体和部分层次的结构中,希望通过一种方式忽略整体与部分的差异,一致的对待它们
- 在一个使用面向对象语言开发的系统中要处理一个树形结构时
- 在一个系统总能够分离出叶子和容器对象,而且它们的类型不固定,需要增加一些新的类型