设计模式之组合模式

组合模式

组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。掌握组合模式的重点是要理解清楚 “部分/整体” 还有 ”单个对象“ 与 "组合对象" 的含义。
组合模式可以让客户端像修改配置文件一样简单的完成本来需要流程控制语句来完成的功能。
经典案例:系统目录结构,网站导航结构等。

模式作用:

  1. 你想表示对象的部分-整体层次结构时
  2. 你希望用户忽略组合对象和单个对象的不同,用户将统一地使用组合结构中的所有对象(方法)

注意事项:

  1. 该模式经常和装饰者模式一起使用,它们通常有一个公共的父类(也就是原型),因此装饰必须支持具有add,remove,getChild操作的component接口

例子:

文件夹和文件之间的关系,非常适合用组合模式来描述.文件夹里既可以包含文件,又可以包含其他文件夹,最终可能组合成一棵树,组合模式在文件夹的应用中有一下两层好处.
例如,我在同事的移动硬盘里找到了一些电子书,想把它们复制到F盘中的学习资料文件夹.在复制这些电子书的时候,我并不需要考虑这批文件的类型,不管它们是单独的电子书还是被放在了文件夹中.组合模式让Ctrl+V,Ctrl+C成为了一个统一的操作.
当我用杀毒软件扫描该文件夹时,往往不会关心里面有多少文件和子文件夹,组合模式使得我们只需要操作最外层的文件夹进行扫描
现在我们来编写代码,首先分别定义好文件夹Folder和文件File这两个类.见如下代码:

/***********Folder***********/
var Folder=function(name){
  this.name=name;
  this.files=[];
}
Folder.prototype.add=function(file){
  this.files.push(file)
}
Folder.prototype.scan=function(){
  console.log("开始扫描文件夹:"+this.name);
  for(var i=0;file=this.files[i];i++){
    file.scan();
  }
}
/***********File***********/
var File=function(name){
  this.name=name;
}
File.prototype.add=function(){
  throw new Error("文件下面不能添加文件");
}
File.prototype.scan=function(){
  console.log("开始扫描文件:"+this.name);
}

接下来创建一些文件夹和文件对象,并且让它们组合成一棵树,这棵树就是我们F盘里的现有文件目录结构:

var folder=new Folder("学习资料");
var folder1=new Folder("JavaScript");
var folder2=new Folder("JQuery");
var file1=new File("JavaScript设计模式与开发实践")
var file2=new File("精通JQuery");
var file3=new File("重构与模式");
folder1.add(file1);
folder2.add(file2);
folder.add(folder1);
folder.add(folder2);
folder.add(file3);

现在的需求是把移动硬盘里的文件和文件夹都复制到这棵树中,假设我们已经得到了这些文件对象:

var folder3=new Folder("Nodejs");
var file4=new File("深入浅出Node.js");
folder3.add(file4);
var file5=new File("JavaScript语言精髓与编程实践");
folder.add(folder3);
folder.add(file5);

通过这个例子,我们再次看到客户是如何同等对待组合对象和叶对象.在添加一批文件的操作过程中,客户不用分辨它们到底是文件还是文件夹.新增加的文件和文件夹能够很容易地添加到原来的树结构中,和树里已有的对象一起工作
我们改变了树的结构,添加了新的数据,却不用修改任何一句原有的代码,这是符合开放-封闭原则的.
运用了组合模式之后,扫描整个文件夹的操作也是轻而易举的,我们只需要操作树的最顶端对象

folder.scan()

执行结果如下如所示:


一些值得注意的地方

  1. 组合模式不是父子关系
    组合模式的树形结构容易让人误以为组合对象和叶对象是父子关系,这是不正确的.
    组合模式是一种HAS-A(聚合)的关系,而不是IS-A.组合对象包含一组叶对象,但Leaf并不是composite的子类.组合对象把请求委托给它所包含的所有叶对象,它们能够合作的关键是拥有相同的接口.
  2. 和叶对象操作的一致性
    组合模式除了要求组合对象和叶对象拥有相同的接口之外,还有一个必要条件,就是对一组叶对象的操作必须具有一致性.
    比如公司要给全体员工发放元旦的过节费1000块,这个场景可以运用组合模式,但如果公司给今天过生日的员工发送一封生日祝福的邮件,组合模式在这里就没有用武之地了,除非先把今天过生日的员工挑选出来.只有用一致的方式对待列表中的每个叶对象的时候,才适合使用组合模式
  3. 双向映射关系
    发放过节费的通知步骤是从公司到各个部门,再到各个小组,最后到每个员工的邮箱里.这本身是个组合模式的好例子,但要考虑的一种情况是,也许某些员工属于多个组织架构.比如某位架构师既隶属于开发组,又隶属于架构组,对象之间的关系并不是严格意义上的层次结构,在这种情况下,是不适合使用组合模式的额,该架构师很可能会收到两份过节费.
    这种复合情况下我们必须给父节点和子节点建立双向映射关系,一个简单的方法是给小组和员工对象都增加集合来保存对方的引用.但是这种相互间的引用相当复杂,而且对象之间产生了过多的耦合性,修改或者删除一个对象都变得困难,此时我们可以引入中介者模式来管理这些对象
  4. 用职责链模式来提高组合模式性能
    在组合模式中,如果树的结构比较复杂,节点数量很多,在遍历树的过程中,性能方面也许表现得不够理想.有时候我们确实可以借助一些技巧,在实际操作中避免遍历整棵树,有一种现成的方案是借助职责链模式.职责链模式一般需要我们手动去设置链条,但在组合模式中,父对象和自对象之间实际上形成了天然的职责链.让请求顺着链条从父对象往子对象传递,或者是反过来从子对象往父对象传递,直到遇到可以处理该请求的对象为止,这也是职责链模式的经典运用场景之一.
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容