JS设计模式——9.组合模式
组合模式概述
组合模式是一种专为创建Web上的动态用户界面量身定制的模式.使用这种模式可以用一条命令在多个对象上激发复杂的递归的行为.
它可以用来把一批子对象组织成树形结构,并且使整棵树都可被遍历.所有组合对象都实现了一个用来获得子对象的方法.
组合对象有两种结构组合对象和叶对象,如下:
组合对象
/ | \
组合对象 叶对象 组合对象
/ \ / \
叶对象 叶对象 叶对象 叶对象
组合模式的适用条件
- 存在一批组织成某种层次体系的对象(具体的结构在开发期间可能无法得知)
- 希望这批对象或其中的一部分对象实施一个操作.
组合模式擅长于对大批对象进行操作.它专为组织这类对象并把操作从一个层次向下一个层次传递而设计.
示例:表单验证
现在我们来设计一个表单,可以保存,恢复和验证其中的数据,但是这个表单中元素的内容和数目都是完全未知的,而且会因用户而异.
我们将input,select,textarea作为叶对象,所有的input组成组合对象,所有的select和textarea组成组合对象,所有的组合对象再组成最终的组合对象.
Task1:创建一个动态表单并实现save和validate操作
我们不想为表彰元素的每一种可能组合编写一种方法,而是决定让这两个方法与表单域自身关联起来.也就是说让每个域都知道如何保存和验证自己.
这里的难点在于如何同时在所有域上执行这些操作.我们不想用迭代的方法,而是用组合模式.
最顶层的组合对象将在其所有子对象上递归调用save方法.
//
var Composite = new Interface('Composite', ['add', 'remove', 'getChild']);
var FormItem = new Interface('FormItem', ['save']);
var CompositeForm = function(id, method, action){
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.getElment());
};
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);
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;
};
//
var Field = function(id){
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){
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);
InputField.prototype.getValue = function(){
return this.input.value;
};
var TextareaField = function(id, label){
Field.call(this, id); this.textarea = document.createElement('textarea');
this.textarea.id = id; this.label = document.createElement('label');
var labelTexrarea = document.createTextNode(label);
this.label.appendChild(labelTexrarea); this.element = document.createElement('div');
this.element.className = 'input-field';
this.element.appendChild(this.label);
this.element.appendChild(this.textarea);
}
extend(TextareaField, Field);
TextareaField.prototype.getValue = function(){
return this.textarea.value;
};
var SelectField = function(id, label){
Field.call(this, id); this.select = document.createElement('select');
this.select.id = id; this.label = document.createElement('label');
var labelTexrarea = document.createTextNode(label);
this.label.appendChild(labelTexrarea); this.element = document.createElement('div');
this.element.className = 'input-field';
this.element.appendChild(this.label);
this.element.appendChild(this.select);
}
extend(SelectField, Field);
SelectField.prototype.getValue = function(){
return this.select.options[this.select.selectedIndex].value;
};
Task2:汇合起来
这是组合模式大放光彩的地方.无论有多少个表单域,对整个组合对象执行操作只需一个函数调用即可.
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));
contactForm.add(new InputField('zip', 'Zip'));
contactForm.add(new TextareaField('comments', 'Comments'));
var stateArray = [{'al': 'Alabama'},...];
addEvent(window, 'unload', contactForm.save);
Task3:向FormItem添加操作
1.修改接口
var FormItem = new Interface('FormItem', ['save', 'restore']);
2.在叶对象类上实现这些操作
Field.prototype.restore = function(){
this.element.value = getCookie(this.id);
};
3.为组合对象添加同样的操作
CompositeForm.prototype.restore = function(){
for(var i= 0, len=this.formComponents.length; i<len; i++){
this.formComponents[i].restore();
}
};
4.注册事件
addEvent(window, 'load', contactForm.restore);
Task4:向层次体系中添加类
到目前为止只有一个组合对象类.如果设计目标要求对操作的调用有更多粒度上的控制,那么,可以添加更多层次的组合对象类,而不必改变其他类.
在层次体系中创建新的层次是一个更好的选择.我们可以把域组织在域集中,每一个域集都是一个实现FormItem接口的组合对象.
var CompositeFieldset = function(id, legendText){
this.components = {};
this.element = document.createElement('fieldset');
this.element.id = id;
if(legendText){
this.legend = document.createElement('legend');
this.legend.appendChild(document.createTextNode(legendText));
this.element.appendChild(this.legend);
}
};
CompositeFieldset.prototype.save = function(){
for(var id in this.components){ //在中间新建了一层,那么就要相应的增加递归的操作,只要不是叶对象,都应该有类似的操作
if(!this.components.hasOwnProperty(id)) continue;
this.components[id].save();
}
};
CompositeFieldset.prototype.restore = function(){
for(var id in this.components){
if(!this.components.hasOwnProperty(id)) continue;
this.components[id].restore();
}
};
CompositeFieldset.prototype.getElement = function(){
return this.element;
};
var nameFieldset = new CompositeFieldset('name-fieldset');
nameFieldset.add(new InputField('first-name', 'First Name'));
nameFieldset.add(new InputField('last-name', 'Last Name'));
contactForm.add(nameFieldset);
现在我们用域集对一部分域进行了组织.也可以直接把域加入表单之中,这是因为表单不在乎子对象究竟是组合对象还是叶对象.
组合模式之利
- 不必编写大量手工遍历数组或其他数据结构的粘合代码.
- 各个对象之间非常松散,促进了代码的重用.
- 形成了一个出色的层次体系,每当顶层组合对象执行一个操作时,实际上是在对整个结构进行深度优先的搜索以查找节点.这样查找,添加和删除都是很容易的.
组合模式之弊
由于组合对象调用的任何操作都会被传递到它的所有子对象,如果这个层次体系很大的话,系统的性能将会受到影响.
JS设计模式——9.组合模式的更多相关文章
- JavaScript设计模式之----组合模式
javascript设计模式之组合模式 介绍 组合模式是一种专门为创建Web上的动态用户界面而量身制定的模式.使用这种模式可以用一条命令在多个对象上激发复杂的或递归的行为.这可以简化粘合性代码,使其更 ...
- C#设计模式(10)——组合模式(Composite Pattern)
一.引言 在软件开发过程中,我们经常会遇到处理简单对象和复合对象的情况,例如对操作系统中目录的处理就是这样的一个例子,因为目录可以包括单独的文件,也可以包括文件夹,文件夹又是由文件组成的,由于简单对象 ...
- JS设计模式——5.单体模式
JS设计模式——5.单体模式 http://www.cnblogs.com/JChen666/p/3610585.html 单体模式的优势 用了这么久的单体模式,竟全然不知!用它具体有哪些好处呢? ...
- c++设计模式15 --组合模式
今天研究了一下设计模式15 组合模式 本人是菜鸟一枚,所以一开始完全不懂组合究竟是什么意思.先上图一张,树形结构图: 文档说,如果想做出这样的结构,通常考虑组合模式.那是为什么呢?现在让我们看一下组合 ...
- 乐在其中设计模式(C#) - 组合模式(Composite Pattern)
原文:乐在其中设计模式(C#) - 组合模式(Composite Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 组合模式(Composite Pattern) 作者:weba ...
- C#设计模式(10)——组合模式(Composite Pattern)(转)
一.引言 在软件开发过程中,我们经常会遇到处理简单对象和复合对象的情况,例如对操作系统中目录的处理就是这样的一个例子,因为目录可以包括单独的文件,也可以包括文件夹,文件夹又是由文件组成的,由于简单对象 ...
- C#设计模式:组合模式(Composite Pattern)
一,C#设计模式:组合模式(Composite Pattern) using System; using System.Collections.Generic; using System.Linq; ...
- js设计模式——7.备忘录模式
js设计模式——7.备忘录模式 /*js设计模式——备忘录模式*/ // 备忘类 class Memento { constructor(content) { this.content = conte ...
- js设计模式——6.模板方法模式与职责链模式
js设计模式——6.模板方法模式与职责链模式 职责链模式
随机推荐
- vue使用axios发送数据请求
本文章是基于vue-cli脚手架下开发 1.安装 npm install axios --s npm install vue-axios --s 2.使用.在index.js中(渲染App组件的那个j ...
- 程序集里包含多个版本dll引用 ,强制低版本到制定版本dll引用
在 config 的 <configuration> 节点内加入以下 类似信息 以下是以Newtonsoft.Json 为例子 <runtime> <assemblyBi ...
- Java并发编程中的设计模式解析(二)一个单例的七种写法
Java单例模式是最常见的设计模式之一,广泛应用于各种框架.中间件和应用开发中.单例模式实现起来比较简单,基本是每个Java工程师都能信手拈来的,本文将结合多线程.类的加载等知识,系统地介绍一下单例模 ...
- Python中用dict统计列表中元素出现的次数
01 Python增加元素,不像其他语言使用现实的操作接口,只需要dict[1]=3,如果字典中不存在1,则直接新增元素键值对(1,3),如果存在则替换键1为3. if key in dict:判断出 ...
- 关于PHP 时区错误的问题
php的ini文件中时区配置默认为关闭状态 这会导致调用时间函数时出错,所以要开启时区并且配置自己的时区: 查询手册找到所有的时区有: 所以修改配置为: 重启apache问题解决
- 安装MySql出现Error Nr.1045的解决办法
如图,最后一步出现这个错误框 这是因为上次安装过MySql,其用户数据在卸载的时候没有被删除掉,解决办法如下: 显示隐藏的文件夹,打开C盘,找到下图文件删除之 删除之后再安装一遍MySQL,就可以了
- BZOJ 1031 [JSOI2007]字符加密Cipher | 后缀数组模板题
BZOJ 1031 [JSOI2007]字符加密Cipher | 后缀数组模板题 将字符串复制一遍接在原串后面,然后后缀排序即可. #include <cmath> #include &l ...
- 解题:AHOI2017/HNOI2017 礼物
题面 先不管旋转操作,只考虑增加亮度这个操作.显然这个玩意的影响是相对于$x,y$固定的,所以可以枚举增加的亮度然后O(1)算出来.为了方便我们把这个操作换种方法表示,只让一个手环改变$[-m,m]$ ...
- 2019PKU\THU WC题解
PKU: 机试: d1t1: 考虑拓扑序的合法性,每个点的入边必须先加入.f[S]表示先出来的是S集合的点,对应边的方案数.加入x的时候,把入边方向确定,出边自然后面会确定的 2^n*n d1t2: ...
- Linux日常维护命令
对于程序员来说,掌握一些基本的Linux命令是必不可少的,即使现在用不到,在不久的将来也应该会用到.由于Linux有很多命令,每个命令基本可以用一篇文章介绍,所以本文仅总结一些常用命令的常用用法,如有 ...