【js设计模式笔记---组合模式】_26.javascript设计模式中的组合模式具有哪些优势-程序员宅基地

组合模式

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

组合模式为操劳过度的javascript程序员带来了两大的好处

你可以用同样的方法处理对象的集合与其中的特定子对象。组合对象与组成它的对象实现了同一批操作。对组合对象执行的这些操作将向下传递到所有的组成对象,这样一来所有的组成对象都会执行同样的操作。在存在大批对象的情况下,这是一种非常有效的技术。

它可以用来把一批子对象组织成树形结构,并且使整棵树都可被遍历。所有组合对象都实现了一个用来获取其子对象的方法。借助这个方法,你可以隐藏实现的细节并随心所欲组织子对象。任何使用这个对象的代码都不会对其内部实现形成依赖。

组合对象的结构

在组合对象的层次体系中有两种类型的对象:叶对象和组合对象。这是一个递归定义,但这正是组合模式如此有用的原因所在。一个组合对象由一些别的组合 对象和叶对象组成。其中只有叶对象不再包含子对象。叶对象是组合对象中最基本的元素,也是各种操作的落实地点。

使用组合模式

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

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

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

组合模式擅长于对大批对象进行操作。它专为组织这类对象并把操作从一个层次向下一层次传递而设计。

示例:表单验证

假设你茹柔吐刚个新项目。乍一看任务很简单:创建一个表单,要求可以保存、恢复和验证其中的值。随便一个半吊子Web开发人员都能搞定,不是吗?问题在于,这个表单元素中的内容和数目都是完全未知的,而且会因用户而异。典型的例子就是,那种紧密耦合到name和address这类特定的表单域的validate函数不会管用,因为在开发期间无法得知要验证哪些域。这是因为开发期间无法得知验证哪些域。这正是组合模式可以大显身手的地方。

首先,我们应该逐一鉴别表单的各个组成元素,判断它属于组合对象还是叶对象。表单最基本的构成要素是用户用以输入数据的域,它们由input、select和textarea标签生成。用于组织相关域的fieldset标签属于上面的一个层次。位于最顶屋的是表单自身。

首先要做的是创建那些组合对象和叶对象需要实现两个接口:

var Composite =new Interface(“Composite”,[“add”,”remove”,”getChild”]);

var FormItem = new Interface(“FormItem”,[“save”]);

CompositeForm类的代码如下:

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.getElement());

}

 

CompositeForm.prototype.remove = function(child){

for(vari=0,len=this.formComponents.length;i<len;i++){

     if(this.formComponents[i]===child){

           this.formComponents.splice(i,1);

            break;

      }

}

}

 

CompositeForm.prototype.getChild = function(child){

return this.formComponents[i];

}

 

 

 

CompositeForm.prototype.save = function(child){

for(vari=0,len=this.formComponents.length;i<len;i++){

     if(this.formComponents[i]===child){

           this.formComponents.save();

      }

}

}

 

CompositeForm.prototype.getElement = function(child){

    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 onthe class Field.’);

};

这个类将被各个叶对象类继承。它将Composite接口中的方法实现了空函数,这是因为叶节点不会有子对象。你也可以考虑让这几个函数异常。

save方法用getValue方法获得所要保存的对象值,后一方法在各个子类中的实现各不相同。使用save方法,不用提交表单也能保存表单的内容。对于这种长长的表单来说这尤其有用,因为用户可以把数据保存下来,等以后再回来完成表单的填写。

var InputField =function(id,label){

Field.call(this,id);

this.input = document.create(‘input’);

this.input.id = id;

this.label = document.create(‘label’);

var labelTextNode = document.createTextNode(label);

 

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;

}

InputField是Field的子类之一。它的大多数方法都是从Field继承而来,但它也实现了针对input标签的getValue方法的代码。TextareaField和SelectField也实现了自己特有的getValue方法:

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(‘div’);

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);

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 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);

SelectField.prototype.getValue= function(){

   returnthis.select.options[this.select.selectedIndex].value;

}

汇合起来

这是组合模式大放光彩的地方 。无论有多少表单域,对整个组合对象执行操作只需要一个函数调用即可:

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

contactForm.add(new InputField(‘firest-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 =[{‘a1’,’Alabama’},…]

contactForm.add(new InputField(‘zip’,”Zip”));

contactForm.add(newTextareaField(‘comments,”Comments”));

 

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

可以把save的调用绑定到某个事件上,也可以用setInterval周期性地调用这个函数。为这个组合对象添加其他操作也很简单。如下一节所示,验证数据、恢复先前保存的数据以及将表单重设为默认状态这些操作都可以如法炮制。

向FormItem添加操作

现在基本框架已经设计好了,要为FormItem接口添加其他操作很容易。首先是修改这个接口:

var FormItem = new Interface(‘FormItem’,[“save”,”restore”]);

然后是在叶对象上实现这些操作。在本例中只要为超类Field添加这些操作以供那些子类继承即可:

Field.prototype.restore= function(){

  this.element.value = getCookie(this.id);

}

最后,为组合对象添加同样的操作:

CompositeForm.prototype.restore= function(){

   for(vari=0,len=this.formComponents.length;i<en;i++){

       this.formComponents[i].restore();

   }

}

在实现中加入下面这行代码后就可以窗口加载时恢复所有表单域的值:

addEvent(window,’load’,contactForm.restore);

向层次体系中添加类

到目前为止只有一个组合对象类。如果设计目标要求对操作的调用 有更多粒度上的控制,那么,可能添加更多层次的组合对象类,而不必改变其类。假设我们想要对表单的某些部分执行save和restore操作,而不影响到其他部分,有一个解决办法是逐一在各个域上执行这些操作:

firestName.restore();

lastName.restore();

但在不知道表单具体会有哪些域的情况下,这种方法并不管用。在层次体系中创建新的层次是一个更好的选择。我们可以把域组织在域集(fieldset)中,每一个域集都是一个实现了FormItem接口的组合对象。在域集上调用restore将导致在其所有子对象上调用restore。

创建CompositeFieldset类并不要求为此修改其他类。因为composite接口隐藏了所有内部实现细节,我们可以自由选用某种数据结构来存储子对象。作为示范,我们在些使用一个对象来存储CompositeFieldset的子对象,而不像CompositeForm那样使用数组:

varCompositeFieldset = function(id,legendText){

    //implements composite,FormItem

     this.components = {};

     this.element = document.createElement(“fieldset”);

     this.element.id = id;

     if(legendText){

           this.legend =document.createElement(‘legend’);

         this.legend.appendChild(document.create.TextNode(legendText);

        this.element.appendChild(this.legend);

     }

}

 

CompositeFieldset.prototype.add= function(child){

  Interface.ensureImplements(child,Composite,FormItem);

   this.components[child.getElement().id] =child;

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

}

 

CompositeFieldset.prototype.remove= function(child){

   delete this.components[child.getElement().id]

}

 

CompositeFieldset.prototype.getChild= function(child){

   if(this.components[id] != undefined){

        return this.components[id];

   }else {

        return null;

    }

}

 

CompositeFieldset.prototype.save= function(child){

   for(var id in this.components){

     if(!this.components.hasOwnProperty(id))continue;

          this.components[id].save();

   }

}

 

CompositeFieldset.prototype.restore= function(child){

   for(var id in this.components){

     if(!this.components.hasOwnProperty(id))continue;

          this.components[id].restore();

   }

}

 

CompositeFieldset.prototype.getElement=function(child){

   return this.element;

}

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/sunscheung/article/details/52682737

智能推荐

浅析C语言的一个关键字——register_register char * yysource-程序员宅基地

文章浏览阅读327次。--------------------- 本文来自 21aspnet 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/21aspnet/article/details/257511?utm_source=copy1、register修饰符暗示编译程序相应的变量将被频繁地使用,如果可能的话,应将其保存在CPU的寄存器中,以加快其存储速度。例如下面的内存块拷贝代码..._register char * yysource

Unity - 通用渲染管线(URP)1.渲染、后处理_urp贴图-程序员宅基地

文章浏览阅读4.1w次,点赞24次,收藏145次。简介The Universal Render Pipeline (URP) is a prebuilt Scriptable Render Pipeline, made by Unity. The technology offers graphics that are scalable to mobile platforms, and you can also use it for higher..._urp贴图

使用 ARFoundation 实现 AR 虚拟试戴_ar试帽项目-程序员宅基地

文章浏览阅读336次。在 Hierarchy 面板中右键点击 “AR Session” 对象,选择 “XR” -> “AR Session Origin”,将其添加为 “AR Session” 的子对象。首先,打开 Unity,并在 Hierarchy 面板中右键点击创建一个空对象,将其命名为 “AR Session”。在 Inspector 面板中选择 “AR Session Origin” 对象,然后在右侧的 Inspector 面板中找到 “AR Session Origin” 组件的 “AR Session” 字段。_ar试帽项目

探索T-Writer.js:一款强大的Web文本编辑器-程序员宅基地

文章浏览阅读320次,点赞3次,收藏6次。探索T-Writer.js:一款强大的Web文本编辑器项目地址:https://gitcode.com/ChrisCavs/t-writer.jsT-Writer.js 是一个基于JavaScript的开源富文本编辑器,旨在提供一种简洁、高效的在线写作体验。它具备现代Web应用所需的多种功能,并且易于集成到你的网站或应用中。技术分析T-Writer.js 使用了最新的Web技术栈,包括:...

[PHP]学生成绩管理系统_php成绩管理系统-程序员宅基地

文章浏览阅读1w次,点赞27次,收藏175次。[PHP]学生成绩管理系统其实,这是我大一的时候,数据库的课程设计,虽然现在回看也觉得代码凌乱,但也懒得改了,之前是发在了自己搭建的博客,现在发到CSDN上来。文章目录[PHP]学生成绩管理系统1 前言2 功能模块2.1学生模块2.2 教师模块2.3 主要的文件结构3 界面设计3.1 登录界面3.2 学生主页(我的成绩)3.3 各科成绩3.4 教师主页(学生管理)3.5 学生信息详情3.6 学生信息修改3.7 添加学生信息3.8 课程管理3.9 各科成绩3.10 一键管理4 数据库设计4.1 E-R图4_php成绩管理系统

基于ssm超市库存商品管理系统的设计与实现(源码+lw+部署文档+讲解等)-程序员宅基地

文章浏览阅读775次,点赞22次,收藏24次。功能对照表的目的是帮助开发团队了解软件的功能状况,及时修复功能缺陷和错误,并提高软件的质量和稳定性。功能编号功能名称功能描述功能状态备注1用户登录用户可以通过提供用户名和密码登录系统正常用户名和密码的验证机制安全性2用户注册用户可以通过提供用户名、密码和电子邮件地址注册新的账户正常无3密码修改用户可以通过提供原密码和新密码修改已有账户的密码正常用户密码的修改操作是否需要提供安全认证4用户信息查看用户可以查看自己的个人信息,如用户名、电子邮件地址、角色等正常无。

随便推点

PostgreSQL中查看/关闭正在执行的SQL、锁和事务_pgsql关闭正在查询的物化视图-程序员宅基地

文章浏览阅读9.8k次,点赞6次,收藏15次。介绍PG查看/关闭链接、查看锁的方式,同时提供了MySQL的类似性能监控和分析_pgsql关闭正在查询的物化视图

华为ospf与思科ospf对比-程序员宅基地

文章浏览阅读1.7k次,点赞3次,收藏10次。1.ospf对比:1)华为:ospf基本配置:系统试图:设置router-id:router id 路由器id系统试图:进入ospf配置试图:ospf进入区域配置试图:area 区域编号将接口网段加入ospf:network 网段 反码返回用户试图:return2)思科:配置ospf命令:启用ospf进程:全局配置模式:router ospf 进程号设置router-id:ospf配置模式:rou..._ensp和cisco ospf对比

11种开源即插即用模块汇总 !!(附论文和代码)_即插即用模块2023-程序员宅基地

文章浏览阅读2.6k次,点赞24次,收藏80次。即插即用的模块就像是一盒乐高,让我们能快速组合各种设计好的模块,搭建出我们需要的模型,这样做不仅让建模速度提升,还保证了模型的创新性和有效性。_即插即用模块2023

vue中的js文件如何使用i18n 国际化_vue中i18n.js文件中如何调用解决,并将结果赋值给i1 8n的message-程序员宅基地

文章浏览阅读5.8k次,点赞2次,收藏6次。1.在main.js文件中引入:方式1://main.jsimport VueI18n from 'vue-i18n'Vue.use(VueI18n) // 通过插件的形式挂载,通过全局方法 Vue.use() 使用插件const i18n = new VueI18n({ locale: 'zh', // 语言标识 //this.$i18n.locale // 通过切换loca..._vue中i18n.js文件中如何调用解决,并将结果赋值给i1 8n的message

linux驱动开发:ft5x06的touch screen的IIC驱动程序编写_触摸屏a类协议和b类协议-程序员宅基地

文章浏览阅读2.8k次。触摸屏属于一个标注的input dev.所以我们按照输入子系统的流程来实现驱动开发。 实际板子与CTPM的通讯连接基于IIC总线,所以我们需要把驱动挂载到IIC总线下面去,也就是注册IIC驱动到iic_core.c中去。 实例化一个IIC设备有多种方式,仿照上一次的24cxx IIC设备的创建,我们来实现ft5x06IIC设备的创建。 因实际板子上TS IC使用的是ft5x02,所以先实例化设_触摸屏a类协议和b类协议

关于html中下拉菜单select的样式的改变_html设置select选择显示和下来显示怎么能不一致-程序员宅基地

文章浏览阅读5.2k次。关于html中下拉菜单select的样式的改变作者:菩提树下的杨过 日期:2006-06-12字体大小: 小 中 大 首先要告诉大家,如果你是用css的方法,除了箭头部分,其他都可以改变,这是很令人别扭的事,因为其他的样式改了,箭头部分改不了等于无用。下面举个css改select的例子 HTML代码 .box{border:1px solid#C0C0_html设置select选择显示和下来显示怎么能不一致

推荐文章

热门文章

相关标签