组合模式

1. 适合使用组合模式的条件:

  • 存在一批组织成某种层次体系的对象,如树形结构(具体的结构在开发期间可能无法得知)
  • 希望对这批对象或其中的一部分对象实施一个相同的操作

2. 注意点:

  • 组合对象与其所有子对象具有相同的接口,但是叶对象并没有继承其上一级组合对象,不是超类与子类的关系

3. 例子:表单信息保存及恢复

可以指定整个表单保存输入数据,及恢复保存的数据,也可以只保存指定的一部分数据

<style>
form {
border: 6px double #9c9c9c;
padding: 10px;
margin: 10px;
}
</style> <body>
<div class="box">
<div class="child"></div>
</div>
<div id="feed-readers"></div>
</body> <script> // 用于实现类似继承的方法,这个是简化版,可以去前面的文章:JavaScript设计模式(1) 查看
function extend(subClass, superClass) {
var F = function() {}
F.prototype = superClass.prototype;
subClass.prototype = new F();
subClass.prototype.constructor = subClass;
} // var Composite = new Interface('Composite', ['add', 'remove', 'getChild'])
// var FormItem = new Interface('FormItem', ['save', 'restore']) // 存储要保存的数据
var DATA = {} /**
* [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 || '#'; var bt1 = document.createElement('button')
var bt2 = document.createElement('button')
bt1.innerHTML = 'save'
bt2.innerHTML = 'restore'
bt1.addEventListener('click', this.save.bind(this))
bt2.addEventListener('click', this.restore.bind(this))
this.element.appendChild(bt1)
this.element.appendChild(bt2)
};
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);
child.getElement().remove()
break;
}
}
};
CompositeForm.prototype.getChild = function(i) {
return this.formComponents[i];
};
CompositeForm.prototype.save = function(e) {
e.preventDefault()
DATA = {}
for(var i=0, len=this.formComponents.length; i<len; i++) {
this.formComponents[i].save() // 组合模式遍历各个子对象并对他们调用同样的方法
}
console.log(DATA)
};
CompositeForm.prototype.restore = function(e) {
e.preventDefault()
for(var i=0, len=this.formComponents.length; i<len; i++) {
this.formComponents[i].restore()
}
}
CompositeForm.prototype.getElement = function() {
return this.element;
}; /**
* [CompositeFieldset 表单域类]
*/
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);
} var bt1 = document.createElement('button')
var bt2 = document.createElement('button')
bt1.innerHTML = 'save'
bt2.innerHTML = 'restore'
bt1.addEventListener('click', this.save.bind(this))
bt2.addEventListener('click', this.restore.bind(this))
this.element.appendChild(bt1)
this.element.appendChild(bt2)
}
CompositeFieldset.prototype.add = function(child) {
// Interface.ensureImplements(child, Composite, FormItem)
this.components[child.id] = child;
this.element.appendChild(child.getElement())
}
CompositeFieldset.prototype.remove = function(child) {
delete this.components[child.getElement().id];
child.getElement().remove()
}
CompositeFieldset.prototype.getChild = function(id) {
if(this.components[id] !== undefined) {
return this.components[id]
}else {
return null
}
}
CompositeFieldset.prototype.getElement = function() {
return this.element;
}
CompositeFieldset.prototype.save = function(e) {
if(e) e.preventDefault()
for(var id in this.components) {
if(!this.components.hasOwnProperty(id)) continue;
this.components[id].save()
}
console.log(DATA)
}
CompositeFieldset.prototype.restore = function(e) {
if(e) e.preventDefault()
for(var id in this.components) {
if(!this.components.hasOwnProperty(id)) continue;
this.components[id].restore()
}
} /**
* [Field 叶子类,实现空方法]
*/
var Field = function(id) {
this.id = id;
this.element = null;
};
Field.prototype.add = function(){};
Field.prototype.remove = function(){};
Field.prototype.getChild = function(){};
Field.prototype.save = function() {
DATA[this.id] = this.getValue()
}
Field.prototype.restore = function() {
this.input.value = DATA[this.id]
}
Field.prototype.getElement = function() {
return this.element;
}
Field.prototype.getVulue = function() {
throw new Error('Unsupported operation on the class Field')
} /**
* [InputField 输入类,继承至 Field]
*/
var InputField = function(id, label) {
Field.call(this, id); this.input = document.createElement('input');
this.input.id = id;
this.label = document.createElement('label');
this.label.innerHTML = label;
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
} // usage
var contactForm = new CompositeForm('contact-form', 'POST', 'www.baidu.com'); var input1 = new InputField('input1', 'input1')
var input2 = new InputField('input2', 'input2')
var input3 = new InputField('input3', 'input3')
var input4 = new InputField('input4', 'input4') var firstFieldset = new CompositeFieldset('first-fieldset', 'first-fieldset')
firstFieldset.add(input1)
firstFieldset.add(input2) var secondFieldset = new CompositeFieldset('second-fieldset', 'second-fieldset')
secondFieldset.add(input3)
secondFieldset.add(input4) contactForm.add(firstFieldset)
contactForm.add(secondFieldset) document.getElementById('feed-readers').appendChild(contactForm.getElement()) </script>

4. 例子:图片库

对照片进行隐藏和显示

<style>
.dynamic-gallery {
display: inline-block;
margin: 30px;
padding: 5px;
border: 1px solid;
}
.gallery-image {
width: 100px;
border: 1px solid;
padding: 5px;
margin: 5px;
}
button {
float: left;
}
</style> <body>
<div id="feed-readers"></div>
</body>
<script> // var Composite = new Interface('Composite', ['add', 'remove', 'getChild']);
// var GalleryItem = new Interface('GalleryItem', ['hide', 'show']); var DynamicGallery = function(id) {
this.children = [];
this.element = document.createElement('div')
this.element.id = id
this.element.className = 'dynamic-gallery';
this.element.appendChild(document.createTextNode(id))
}
DynamicGallery.prototype = {
add: function(child) {
// Interface.ensureImplement(chlid, Composite, GalleryItem);
this.children.push(child);
this.element.appendChild(child.getElement())
},
remove: function(child) {
for(var i=0; i<this.children.length ;i++) {
if(this.getChild(i) == child) {
this.children.splice(i, 1);
child.remove()
break;
}
}
},
getChild: function(i) {
if(this.children[i] !== undefined) {
return this.children[i]
}else {
return null
}
},
hide: function() {
for(var i=0; i<this.children.length; i++) {
this.getChild(i).hide()
}
// this.element.style.display = 'none'
},
show: function() {
// this.element.style.display = 'inline-block'
for(var i=0; i<this.children.length; i++) {
// if(this.getChild(i) instanceof GalleryImage)
this.getChild(i).show() }
},
getElement: function() {
return this.element
}
} var GalleryImage = function(src) {
this.element = document.createElement('img');
this.element.className = 'gallery-image';
this.element.src = src;
}
GalleryImage.prototype = {
add: function(){},
remove: function(){
this.element.remove()
},
getChild: function(){}, hide: function() {
this.element.style.display = 'none'
},
show: function() {
this.element.style.display = ''
},
getElement: function() {
return this.element;
}
} var img1 = new GalleryImage('./images/bg4.jpg')
var img2 = new GalleryImage('./images/bg4.jpg')
var img3 = new GalleryImage('./images/bg4.jpg')
var img4 = new GalleryImage('./images/bg4.jpg')
var img5 = new GalleryImage('./images/bg4.jpg')
var img6 = new GalleryImage('./images/bg4.jpg')
var img7 = new GalleryImage('./images/bg4.jpg')
var img8 = new GalleryImage('./images/bg4.jpg') var firstGallery = new DynamicGallery('first-gallery');
firstGallery.add(img1)
firstGallery.add(img2) var secondGallery = new DynamicGallery('second-gallery');
secondGallery.add(img3)
secondGallery.add(img4) var fourGallery = new DynamicGallery('fourGallery-gallery');
fourGallery.add(img5)
fourGallery.add(img6) firstGallery.add(fourGallery) var wrap = new DynamicGallery('wrap')
wrap.add(img7)
wrap.add(img8)
wrap.add(firstGallery)
wrap.add(secondGallery) document.getElementById('feed-readers').appendChild(wrap.getElement())
wrap.hide() </script>

5. 组合模式的利与弊

  • 利:

    • 不必编写大量手工遍历数组或其他数据结构的粘合代码,只需对最顶层对象执行操作就能把操作传递下去
    • 各个对象之间的耦合非常松散,这促进了代码的重用,也有利于代码重构
    • 用组合模式组织起来的对象形成了一个出色的层次体系,在这个层次体系中添加,删除和查找结点都非常容易
  • 弊:
    • 如果层次体系结构很大的话,系统的性能将会受到影响。
    • 如果组合对象类和节点类都被用作 HTML 元素的包装工具,则需要准守 HTML 的使用规则来进行组合对象的嵌套
    • 需要进行接口检查,确保添加的组合对象实现了对应的方法

注意

转载、引用,但请标明作者和原文地址

JavaScript设计模式(5)-组合模式的更多相关文章

  1. JavaScript设计模式之----组合模式

    javascript设计模式之组合模式 介绍 组合模式是一种专门为创建Web上的动态用户界面而量身制定的模式.使用这种模式可以用一条命令在多个对象上激发复杂的或递归的行为.这可以简化粘合性代码,使其更 ...

  2. 再起航,我的学习笔记之JavaScript设计模式15(组合模式)

    组合模式 组合模式(Composite): 又称部分-整体模式,将对象组合成树形结构以表示"部分整体"的层次结构.组合模式使得用户对单个对象和组合对象的使用具有一致性. 如果有一个 ...

  3. JavaScript设计模式-14.组合模式实现

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  4. JavaScript设计模式-13.组合模式

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  5. 从ES6重新认识JavaScript设计模式(三): 建造者模式

    1 什么是建造者模式? 建造者模式(Builder)是将一个复杂对象的构建层与其表示层相互分离,同样的构建过程可采用不同的表示. 建造者模式的特点是分步构建一个复杂的对象,可以用不同组合或顺序建造出不 ...

  6. C#设计模式(10)——组合模式(Composite Pattern)

    一.引言 在软件开发过程中,我们经常会遇到处理简单对象和复合对象的情况,例如对操作系统中目录的处理就是这样的一个例子,因为目录可以包括单独的文件,也可以包括文件夹,文件夹又是由文件组成的,由于简单对象 ...

  7. c++设计模式15 --组合模式

    今天研究了一下设计模式15 组合模式 本人是菜鸟一枚,所以一开始完全不懂组合究竟是什么意思.先上图一张,树形结构图: 文档说,如果想做出这样的结构,通常考虑组合模式.那是为什么呢?现在让我们看一下组合 ...

  8. 乐在其中设计模式(C#) - 组合模式(Composite Pattern)

    原文:乐在其中设计模式(C#) - 组合模式(Composite Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 组合模式(Composite Pattern) 作者:weba ...

  9. C#设计模式(10)——组合模式(Composite Pattern)(转)

    一.引言 在软件开发过程中,我们经常会遇到处理简单对象和复合对象的情况,例如对操作系统中目录的处理就是这样的一个例子,因为目录可以包括单独的文件,也可以包括文件夹,文件夹又是由文件组成的,由于简单对象 ...

随机推荐

  1. POJ 3590 The shuffle Problem [置换群 DP]

    传送门 $1A$太爽了 从此$Candy?$完全理解了这种$DP$做法 和bzoj1025类似,不过是求最大的公倍数,并输出一个字典序最小的方案 依旧枚举质因子和次数,不足的划分成1 输出方案从循环长 ...

  2. IntelliJ IDEA使用心得之插件篇

    今天和大家分享下插件的安装方法,顺便推荐几个非常好用的插件. 1.安装插件 在工具栏中,点击进入IDE设置界面. 插件仓库界面: 值得注意的是,每次安装/卸载插件后,需要重启IDE. 2.好用的插件 ...

  3. Windows Server 2016-部署额外域控制器

    我们一般所提到的所谓域外控制器,是指除域内第一台域控制器之外的其他域控制器.额外域控制器很多时候也有辅助域控一称.那么在同一域内安装多台域控制器有什么优点呢: 1.提高用户登录效率.多台域控制器可以同 ...

  4. zend framework框架学习走起——从零开始,点击记录-安装

    zend framework第一步,先来安装下zend framework框架. 先介绍下,我这边的php配置环境,为图省事,我是安装wampserver来搭载php环境的,,php+Apache+m ...

  5. [Python Study Notes]CS架构远程访问获取信息--Client端

    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ...

  6. Hive metastore表结构设计分析

    今天总结下,Hive metastore的结构设计.什么是metadata呢,对于它的描述,可以理解为数据的数据,主要是描述数据的属性的信息.它是用来支持如存储位置.历史数据.资源查找.文件记录等功能 ...

  7. CSS3动画中的animation-timing-function效果演示

    CSS3动画(animation)属性有如下几个: 属性 值 说明 animation-name name 指定元素要使用的keyframes名称 animation-duration time(ms ...

  8. PHP中的http协议

    PHP中的http协议 http协议基本组成 报文:用于Http协议交互的信息 请求行:包括请求的方法,URL和HTTP协议版本 状态行:包括响应结果的状态码,状态描述和Http版本 首部字段:包括请 ...

  9. Linux双网卡搭建NAT服务器之网络应用

    一:拓扑.网络结构介绍 Eth1 外网卡的IP 地址, GW和DNS 按照提供商提供配置.配置如下: IP:114.242.25.18 NETMASK:255.255.255.0 GW:114.242 ...

  10. echarts legend 重叠 (转载)

    解决方案:  1. 调整option中的grid.top值才能避免重叠:(可以设置定制,也可以定义了一个计算公式) 2. 文档注明[特殊字符串 ''(空字符串)或者 '\n' (换行字符串)用于图例的 ...