《javascript设计模式》笔记之第九章:组合模式

之前一直都是按照书的结构顺序做总结,觉得好像不是很好,现在试着完全按照自己的理解做总结。例子还是书上的例子。

一:组合模式的作用

在web开发中,主要用于创建嵌套的html结点,使得我们方便的把各种结点连接起来,并且提供简易的操作。

 

二:组合模式的结构

结构就像我们的文件结构一样讲Composite理解为文件夹,Leaf理解为文件就好理解了。

 

三:例子一,创建一个组合的表单

需求:试想着我们想要构建一个表单,但是表单域经常要被产品经理修改,我们怎样才能利用js快速的搭建这个form呢?此外,我们有个功能,就是可以保存用户填写的表单,那么我们一般的做法是手动把一个个表单域保存到cookie中,如何才能通过一个操作就完成所有表单域的保存呢?

 

利用组合模式实现是这样的:

 

思路:创建一个组合表单类CompositeForm,这个类有操作表单域的方法add(),和remove(),传入的参数就是对应的表单域类(InputField,TextareaField等),这样我们就可以很方便的组合表单了。对于保存表单域的功能,我们只要给CompositeForm弄一个save()方法就可以了,这个save()方法就遍历它下面的表单域,自动的保存每个表单域。

 

具体实现:

步骤一:

创建接口,保证表单和表单域都有必须的方法

var Composite = new Interface(‘Composite‘, [‘add‘, ‘remove‘, ‘getChild‘]);
var FormItem = new Interface(‘FormItem‘, [‘save‘]);

步骤二:

定义组合表单类CompositeForm

var CompositeForm = function(id, method, action) { // implements Composite, FormItem
  this.formComponents = [];

  this.element = document.createElement(‘form‘);
  this.element.id = id;
  this.element.method = method || ‘POST‘;
  this.element.action = action || ‘#‘;
};

CompositeForm.prototype.add = function(child) {
  Interface.ensureImplements(child, Composite, FormItem);
  this.formComponents.push(child);
  this.element.appendChild(child.getElement());
};

CompositeForm.prototype.remove = function(child) {
  for(var i = 0, len = this.formComponents.length; i < len; i++) {
    if(this.formComponents[i] === child) {
      this.formComponents.splice(i, 1); // Remove one element from the array at
                                        // position i.
      break;
    }
  }
};

CompositeForm.prototype.getChild = function(i) {
  return this.formComponents[i];
};

CompositeForm.prototype.save = function() {
  for(var i = 0, len = this.formComponents.length; i < len; i++) {
    this.formComponents[i].save();
  }
};

CompositeForm.prototype.getElement = function() {
  return this.element;
};

步骤三:

创建Field类,用来派生出表单域的类

var Field = function(id) { // implements Composite, FormItem
  this.id = id;
  this.element;
};

Field.prototype.add = function() {};
Field.prototype.remove = function() {};
Field.prototype.getChild = function() {};

Field.prototype.save = function() {
  setCookie(this.id, this.getValue);
};

Field.prototype.getElement = function() {
  return this.element;
};

Field.prototype.getValue = function() {
  throw new Error(‘Unsupported operation on the class Field.‘);
};

步骤三

创建特定的表单域类

var InputField = function(id, label) { // implements Composite, FormItem
  Field.call(this, id);

  this.input = document.createElement(‘input‘);
  this.input.id = id;

  this.label = document.createElement(‘label‘);
  var labelTextNode = document.createTextNode(label);
  this.label.appendChild(labelTextNode);

  this.element = document.createElement(‘div‘);
  this.element.className = ‘input-field‘;
  this.element.appendChild(this.label);
  this.element.appendChild(this.input);
};
extend(InputField, Field); // Inherit from Field.

InputField.prototype.getValue = function() {
  return this.input.value;
};

/* TextareaField class. */

var TextareaField = function(id, label) { // implements Composite, FormItem
  Field.call(this, id);

  this.textarea = document.createElement(‘textarea‘);
  this.textarea.id = id;

  this.label = document.createElement(‘label‘);
  var labelTextNode = document.createTextNode(label);
  this.label.appendChild(labelTextNode);

  this.element = document.createElement(‘div‘);
  this.element.className = ‘input-field‘;
  this.element.appendChild(this.label);
  this.element.appendChild(this.textarea);
};
extend(TextareaField, Field); // Inherit from Field.

TextareaField.prototype.getValue = function() {
  return this.textarea.value;
};

/* SelectField class. */

var SelectField = function(id, label) { // implements Composite, FormItem
  Field.call(this, id);

  this.select = document.createElement(‘select‘);
  this.select.id = id;

  this.label = document.createElement(‘label‘);
  var labelTextNode = document.createTextNode(label);
  this.label.appendChild(labelTextNode);

  this.element = document.createElement(‘div‘);
  this.element.className = ‘input-field‘;
  this.element.appendChild(this.label);
  this.element.appendChild(this.select);
};
extend(SelectField, Field); // Inherit from Field.

SelectField.prototype.getValue = function() {
  return this.select.options[this.select.selectedIndex].value;
};

步骤四:

使用

var contactForm = new CompositeForm(‘contact-form‘, ‘POST‘, ‘contact.php‘);

contactForm.add(new InputField(‘first-name‘, ‘First Name‘));
contactForm.add(new InputField(‘last-name‘, ‘Last Name‘));
contactForm.add(new InputField(‘address‘, ‘Address‘));
contactForm.add(new InputField(‘city‘, ‘City‘));
contactForm.add(new SelectField(‘state‘, ‘State‘, stateArray)); // var stateArray =
    [{‘al‘, ‘Alabama‘}, ...]
contactForm.add(new InputField(‘zip‘, ‘Zip‘));
contactForm.add(new TextareaField(‘comments‘, ‘Comments‘));

addEvent(window, ‘unload‘, contactForm.save);

点评:注意CompositeForm里面有formComponents这个属性,里面包含的是每一个表单域的对象,这样的话当我们要用到他们的时候就不用操作DOM来获取了。save()这个方法可以看一下,具体做法就是遍历表单的组件,逐一调用它们的save()方法。由于表单的结构要求,表单域不能嵌套form元素,所以这个例子还不能很好的诠释组合模式,最好情况下组合模式的组合程度是很高的。步骤四中的使用方法中,我们可以发现表单的动态创建是很简单的,保存方法的使用也是很简单,只要操作最contactForm这个变量就可以了。

书上还有一个对这个表单的扩展,这里就不展开了。

四:例子二,Image gallery

下面来看一个更好的例子:

需求:我们要创建一个图片集,图片集里面有图片集和图片,图片集下面又有图片集和图片。

思路:创建一个DynamicGallery类,这个类有add()、remove()等方法。add()方法可以传入DynamicGallery类和GalleryImage类。

 

实现:

步骤一:创建接口,保证上面提到的两个类符合要求

var Composite = new Interface(‘Composite‘, [‘add‘, ‘remove‘, ‘getChild‘]);
var GalleryItem = new Interface(‘GalleryItem‘, [‘hide‘, ‘show‘]);

步骤二:创建DynamicGallery类

var DynamicGallery = function(id) { // implements Composite, GalleryItem
  this.children = [];

  this.element = document.createElement(‘div‘);
  this.element.id = id;
  this.element.className = ‘dynamic-gallery‘;
}

DynamicGallery.prototype = {

  // Implement the Composite interface.

  add: function(child) {
    Interface.ensureImplements(child, Composite, GalleryItem);
    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.formComponents[i].splice(i, 1);
        break;
      }
    }
    this.element.removeChild(child.getElement());
  },
  getChild: function(i) {
    return this.children[i];
  },

  // Implement the GalleryItem interface.

  hide: function() {
    for(var node, i = 0; node = this.getChild(i); i++) {
      node.hide();
    }
    this.element.style.display = ‘none‘;
  },
  show: function() {
    this.element.style.display = ‘block‘;
    for(var node, i = 0; node = this.getChild(i); i++) {
      node.show();
    }
  },

  // Helper methods.

  getElement: function() {
    return this.element;
  }
};

第三步:创建GalleryImage类

var GalleryImage = function(src) { // implements Composite, GalleryItem
  this.element = document.createElement(‘img‘);
  this.element.className = ‘gallery-image‘;
  this.element.src = src;
}

GalleryImage.prototype = {

  // Implement the Composite interface.

  add: function() {},       // This is a leaf node, so we don‘t
  remove: function() {},    // implement these methods, we just
  getChild: function() {},  // define them.

  // Implement the GalleryItem interface.

  hide: function() {
    this.element.style.display = ‘none‘;
  },
  show: function() {
    this.element.style.display = ‘‘; // Restore the display attribute to its
                                     // previous setting.
  },

  // Helper methods.

  getElement: function() {
    return this.element;
  }
};

第四步:使用

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 DynamicGallery(‘vacation-photos‘);

for(var i = 0; i < 30; i++) {
  vacationPhotos.add(new GalleryImage(‘/img/vac/image-‘ + i + ‘.jpg‘));
}

topGallery.add(vacationPhotos);
topGallery.show();      // Show the main gallery,
vacationPhotos.hide();  // but hide the vacation gallery.

总结:在这个例子中,我们就可以把DynamicGallery当作是上面那个结构图的Composite,GalleryImage当作是Leaf来理解了。DynamicGallery的add方法可以添加DynamicGallery,这样我们就可以创造出不同的结构了

 

时间: 2024-10-11 13:29:48

《javascript设计模式》笔记之第九章:组合模式的相关文章

设计模式之第22章-组合模式(Java实现)

设计模式之第22章-组合模式(Java实现) “鱼哥,有没有什么模式是用来处理树形的“部分与整体”的层次结构的啊.”“当然”“没有?”“有啊.别急,一会人就到了.” 组合模式之自我介绍 “请问你是?怎么什么都不说就直接上来了.”“本式行不更名坐不改姓,就是组合模式来着,此次受作者之邀来讲讲我的前世今生来着.”“哦,你就是组合模式啊,久仰久仰.”“失敬失敬.”恩,首先我先说下定义:Compose objects into tree structure to represent part-whole

Javascript设计模式理论与实战:组合模式

我们平时开发过程中,一定会遇到这种情况:同时处理简单对象和由简单对象组成的复杂对象,这些简单对象和复杂对象会组合成树形结构,在客户端对其处理的时候要保持一致性.比如电商网站中的产品订单,每一张产品订单可能有多个子订单组合,比如操作系统的文件夹,每个文件夹有多个子文件夹或文件,我们作为用户对其进行复制,删除等操作时,不管是文件夹还是文件,对我们操作者来说是一样的.在这种场景下,就非常适合使用组合模式来实现. 基本知识 组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对

读书笔记_java设计模式深入研究 第九章 访问者模式 Vistor

1,访问者模式的目的是封装一些施加于某种数据结构元素之上的操作,一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变.为不同类型的元素提供多种访问操作方式,且可以在不修改原有系统的情况下增加新的操作方式,这就是访问者模式的动机. 2,UML图: 3,访问者角色: IElement:抽象的事物元素功能接口,定义了固定功能方法以及可变功能方法接口. Element:具体功能的实现类. IVisitor:访问者接口,为所有访问者对象声明一个visit方法,用来代表为对象结构添加的功能,原则上可

第 15 章 组合模式【Composite Pattern】

以下内容出自:<<24种设计模式介绍与6大设计原则>> 大家在上学的时候应该都学过“数据结构”这门课程吧,还记得其中有一节叫“二叉树”吧,我们上 学那会儿这一章节是必考内容,左子树,右子树,什么先序遍历后序遍历什么,重点就是二叉树的的遍历,我还记得当时老师就说,考试的时候一定有二叉树的构建和遍历,现在想起来还是觉的老师是正确的,树状结果在实际项目应用的非常广泛. 咱就先说个最常见的例子,公司的人事管理就是一个典型的树状结构,你想想你公司的结构是不是这样: 老大,往下一层一层的管理,

第9章 组合模式(Composite Pattern)

原文 第9章 组合模式(Composite Pattern) 概述: 组合模式有时候又叫做部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦. 将对象组合成树形结构以表示"部分-整体"的层次结构.Composite模式使得用户对单个对象和组合对象的使用具有一致性.[GOF <设计模式>] 结构图: 举例: 假设我们公司有做个打卡的程序,能满足各种打卡的需要,比如整个

设计模式之第17章-备忘录模式(Java实现)

设计模式之第17章-备忘录模式(Java实现) 好男人就是我,我就是曾小贤.最近陈赫和张子萱事件闹得那是一个沸沸扬扬.想想曾经每年都有爱情公寓陪伴的我现如今过年没有了爱情公寓总是感觉缺少点什么.不知道你们可曾记得爱情公寓里的一个经典的桥段~每次关谷和唐悠悠吵架的时候,总是可以进行“存档”,先干其他的事情,而后有时间的时候再继续“读档”,这是多么好的一个技能啊,想想吧,每次吵架,存档后可以做其他事情进行冷静一下,然后读档的时候已经冷静好了,是不是会清醒很多呢,是不是就不会有那么多的误会无法解除了呢

设计模式之第9章-原型模式(Java实现)

设计模式之第9章-原型模式(Java实现) “快到春节了,终于快放假了,天天上班好累的说.”“确实啊,最近加班比较严重,项目快到交付了啊.”“话说一到过节,就收到铺天盖地的短信轰炸,你说发短信就发吧,大多数还是一样的,群发.”“就是就是,上次我收到一个,竟然连名字都给弄错了,简直没法说啊,要不然就不发得了,干嘛弄得那么麻烦.”“所以说,才会有群发的短信我不回这个段子嘛.”“对了,今天你不是就要讲那个原型模式什么的,就是类似于群发的是吧.”“嘿嘿,天机不可泄露.”(PS:还天机不可泄露,学会吊起胃

设计模式之第6章-迭代器模式(Java实现)

设计模式之第6章-迭代器模式(Java实现) “我已经过时了,就不要讲了吧,现在java自带有迭代器,还有什么好讲的呢?”“虽然已经有了,但是具体细节呢?知道实现机理岂不美哉?”“好吧好吧.”(迭代器闷闷不乐的答应下来.作者吃着小笼包,咂咂嘴道:哼,想偷懒,窗户都没有~). 迭代器模式之自我介绍 正如你们所见,我目前已经没落了,基本上没人会单独写一个迭代器,除非是产品性质的研发,我的定义如下:Provide a way to access the elements of an aggregate

APUE学习笔记:第九章 进程关系

9.1 引言 本章将更详尽地说明进程组以及POSIX.1引入的会话的概念.还将介绍登陆shell(登录时所调用的)和所有从登陆shell启动的进程之间的关系. 9.1 终端登陆 系统管理员创建通常名为/etc/ttys的文件,其中每个终端设备都有一行,每一行说明设备名传递给getty程序的参数.当系统自举时,内核创建进程ID为1的进程,依旧是init进程.init进程使系统进入多用户状态.init进程读文件/etc/ttys,对每一个允许登陆的终端设备,init调用一次fork,所生成的子进程则

设计模式总结篇系列:组合模式(Composite)

在探讨Java组合模式之前,先要明白几个概念的区别:继承.组合和聚合. 继承是is-a的关系.组合和聚合有点像,有些书上没有作区分,都称之为has-a,有些书上对其进行了较为严格区分,组合是contains-a关系,聚合是has-a关系. 组合方式中被组合的对象生命周期不能超过整体,一般写代码时是直接在整体类的构造方法中创建被组合类的对象.如人和手之间的关系,人都没了,还何来手? 聚合方式中对于对象的生命周期则没有此类限制,一般可以在聚合类的构造函数中通过外部传参以赋值给整体(或通过其他set等