组合模式可以让我们使用树形方式创建对象的结构,我们可以把相同的操作应用在组合对象和单个对象上。在大多数情况下,我们都可以忽略掉组合对象和单个对象之间的差异,从而用一致的方式来处理它们。
例子
以一个扫描文件夹为例。文件夹中可以有文件,也可以包含其他文件夹,最终可能组成一棵树;文件或者文件夹也有可能被删除,要保证内容同步。
var Folder = function( name ){
this.name = name;
this.parent = null;
this.files = [];
}
Folder.prototype.add = function( file ){
file.parent = this;
this.files.push(file);
}
Folder.prototype.scan = function(){
console.log( "开始扫描文件夹:" + this.name );
for( var i=0, file, files=this.files; file=files[i++]; ){
file.scan();
}
}
Folder.prototype.remove = function(){
if(!this.parent){
return;
}
for( var files=this.parent.files, l=files.length-1; l>=0; l-- ){
var file = files[ l ];
if( file ===this ){
files.splice( l, 1 );
}
}
}
var File = function( name ){
this.name = name;
this.parent = null;
}
File.prototype.add = function(){
throw new Error( "不能添加在文件下面" );
}
File.prototype.scan = function(){
console.log("开始扫描文件:" + this.name );
}
File.prototype.remove = function(){
if(!this.parent){
return;
}
for( var files=this.parent.files, l=files.length-1; l>=0; l-- ){
var file = files[ l ];
if( file ===this ){
files.splice( l, 1 );
}
}
}
var folder = new Folder( '学习资料' );
var folder1 = new Folder( 'JavaScript' );
var file1 = new Folder( '深入浅出Node.js' );
folder1.add( new File('JavaScript设计模式') );
folder.add( folder1 );
folder.add( file1 );
folder.scan(); // 学习资料 JavaScript JavaScript设计模式 深入浅出Node.js
folder1.remove();
folder.scan(); // 学习资料 深入浅出Node.js
Folder是组合对象,File是叶子对象,我们对两者同等对待。
注意的地方
组合模式不是父子关系
组合模式是一种HAS-A(聚合)的关系,而不是IS-A。组合对象包含一组叶对象,但是Leaf并不是Composite的子类。对叶对象操作的一致性
组合模式除了要求组合对象和叶对象拥有相同的接口之外,还有一个必要条件,就是对一组叶对象的操作必须具有一致性。
如给全替员工发过节费是可以运用组合模式,但是如果给公司今天过生日的人发祝福邮件,组合模式就不适合了,除非把这些人先挑出来。双向映射关系
发过节费的步骤是:公司通知部门,再到各小组,最后到员工手里。这是一个很适合组合模式的场景,但要考虑一种情况是有些员工隶属多个组织架构,那么采用组合模式可能让他们收到多份过节费。用职责链模式提高组合模式性能
待议。