JavaScript 设计模式之组合模式解析

  怎么说呢?!就像是动物(组合对象)一样,当它生下后代(叶对象)时,它的后代就有了某种功能(比如:挖洞,听力好等等);也像是一棵树,它有一个根(组合对象)然后是从这个棵树向外冒出的其他枝杆(组合对象)以及从这些枝杆又向外长的叶子(叶对象)。换句话说,就是当祖先已经有了,那么只要从这个祖先衍生出来的其他孩子(包括这个祖先下的其他组合对象)已经就具备了某种功能,看上去貌似又有些像是继承。“组合模式”在组合对象的层次体系中有两种类型的对象:叶对象和组合对象。组合模式擅长于对大批对象进行操作。

  “组合模式”就是在做一个项目的时候,我们要把在这个项目中即将出现的方法都在组合对象中定义(包括叶对象中的方法),而它们的叶对象将会继承组合对象。当组合对象被实例化后其叶对象的方法也相应地被实例化了。可能我说的有些乱,下面就用一个例子来说明吧。

  “组合模式”是一种专为创建Web上的动态用户界面而量身定制的模式。使用这种模式,可以用一条命令在多个对象上激发复杂的或递归的行为。

  使用“组合模式”可以为我们带来两大好处:

  1、可以用同样的方法处理对象的集合与其中的特定子对象。

  2、可以用来把一批子对象组织成树形结构,并且使整棵树都可以被遍历。

  只有同时具备以下两个条件进才适合使用组合模式:

  1、存在一批组织成某处层次体系的对象(具体结构可能在开发期间无法知道)。

  2、希望对这批对象或其中的一部分对象实话一个操作。

  下面看一下示例:

  具体要求是做一个图片库且可以有选择地隐藏或显示图片库的特定部分。这可能是单独图片,也可能是图片库。现在就需要两个类来完成这个功能:用作图片库的组合对象类和用于图片本身的叶对象类,代码如下:

  在上面的代码中,首先定义的是组合对象类和叶对象类应该实现的接口。除了常规的组合远景一分钱上,这些类要怕硬欺软 操作只包括hide和show。接下来我们定义叶对象。叶对象实现hide和show,代码如下:

  

复制代码 代码如下:

  var Composite = new Interface('Composite', ['add', 'remove', 'getChild']); // 检查组合对象Composite应该具备的方法

  var GalleryItem = new Interface('GalleryItem', ['hide', 'show']); // 检查组合对象GalleryItem应该具备的方法

  // DynamicGallery Class

  var DynamicGallery = function(id){    // 实现Composite,GalleryItem组合对象类

  this.children = [];

  this.element = document.createElement('div');

  this.element.id = id;

  this.element.className = 'dynamic-gallery';

  }

  DynamicGallery.prototype = {

  // 实现Composite组合对象接口

  add : function(child){

  Interface.ensureImplements(child, Composite, DynamicGallery);

  this.children.push(child);

  this.element.appendChild(child.getElement());

  },

  remove : function(child){

  for(var node, i = 0; node = this.getChild(i); i++){

  if(node == child){

  this.children.splice(i, 1);

  break;

  }

  }

  this.element.removeChild(child.getElement());

  },

  getChild : function(i){

  return this.children[i];

  },

  // 实现DynamicGallery组合对象接口

  hide : function(){

  for(var node, i = 0; node = this.getChild(i); i++){

  node.hide();

  }

  this.element.style.display = 'none';

  },

  show : functioln(){

  this.element.style.display = 'block';

  for(var node, i = 0; node = getChild(i); i++){

  node.show();

  }

  },

  // 帮助方法

  getElement : function(){

  return this.element;

  }

  }

  以下是设置叶对象的相应方法:

  

复制代码 代码如下:

  // GalleryImage class

  var GalleryImage = function(src){    // 实现Composite和GalleryItem组合对象中所定义的方法

  this.element = document.createElement('img');

  this.element.className = 'gallery-image';

  this.element.src = src;

  }

  GalleryImage.prototype = {

  // 实现Composite接口

  // 这些是叶结点,所以我们不用实现这些方法,我们只需要定义即可

  add : function(){},

  remove : function(){},

  getChild : function(){},

  // 实现GalleryItem接口

  hide : function(){

  this.element.style.display = 'none';

  },

  show : function(){

  this.element.style.display = '';

  },

  // 帮助方法

  getElement : function(){

  return this.element;

  }

  }

  这是一个演示组合模式的工作方式的例子。每个类都很简单,但由于有了这样一种层次体系,我们就可以执行一些复杂操作。GalleryImage类的构造函数会创建一个image元素。这个类定义中的其余部分由空的组合对象方法(因为这是叶结点)和GalleryItem要求的操作组成。现在我们可以使用这两个类来管理图片:

  

复制代码 代码如下:

  var topGallery = new DynamicGallery('top-gallery');

  topGallery.add(new GalleryImage('/img/image-1.jpg'));

  topGallery.add(new GalleryImage('/img/image-2.jpg'));

  topGallery.add(new GalleryImage('/img/image-3.jpg'));

  var vacationPhotos = new DyamicGallery('vacation-photos');

  for(var i = 0, i < 30; i++){

  vacationPhotos.add(new GalleryImage('/img/vac/image-' + i + '.jpg'));

  }

  topGallery.add(vacationPhotos);

  topGallery.show();

  vacationPhotos.hide();

  组合模式之利,使用组合模式,简单的操作也能产生复杂的结果。不必编写大师手工遍历数组或其他数据结构的粘合代码,只需对最顶层的对象执行操作,主每一个子对象自己传递这个操作即可。这对于那些再三执行的操作尤其有用。在组合模式中,各个对象之间的耦合非常松散。每当对顶层组合对象执行一个操作时,实际上是在对整个结构进行尝试优先的搜索以查找节点。

  组合模式之弊,由于对组合模式调用的任何操作都会被颇佳北至 它的所有子对象,如果这个层次体系很大的话,系统的性能将会受到影响。