设计模式(10)[JS版]-JavaScript如何实现组合模式???
1 什么是组合模式
组合模式允许创建具有属性的对象,这些对象是原始项目或对象集合。集合中的每个项目本身可以容纳其他集合,创建深度嵌套结构。
树型控件是复合模式的一个完美例子。树的节点要么包含一个单独的对象(叶子节点),要么包含一组对象(节点的子树)。组合模式用于简单化,一致化对单组件和复合组件的使用,其实它就是一棵树。组合模式能对于工作能起到简化作用,组合对象实现某一操作时,通过递归,向下传递到所有的组成对象,在存在大批对象时,假如页面的包含许多拥有同样功能的对象,只需要操作组合对象即可达到目标。在存在着某种的层次结构,并且其中的一部分要实现某些操作,即可使用组合模式。
组合模式中的所有节点都共享一组通用的属性和方法,它既支持单个对象,也支持对象集合。这种共同的接口极大地促进了递归算法的设计和构建,这种算法可以对复合集合中的每个对象进行迭代。
实例场景:
1 自然界中的各种树,树长在大地上,树头(树根),即是入口点,这棵树头向上生长,即有自己的叶子,又有自己的子树枝,某树枝还有自己的叶子,跟子树枝。
2 操作系统目录结构、公司部门组织架构、国家省市县等,像这么看起来复杂的现象,都可以使用组合模式,即部分-整体模式来操作。
2 主要参与者
参与该模式的对象有:
Component :声明组成中对象的接口。
Leaf :代表构图中的叶子对象,一个叶子没有子对象。
Composite :表示组成中的分支(或子树),维护一个子组件的集合。
3 代码实现
在下边的代码中,Node(节点)对象创建了一个树状结构。每个节点都有一个名字和4个方法:add、remove、getChild和hasChildren。这些方法被添加到Node的原型中。这减少了对内存的要求,因为这些方法现在被所有节点共享。Node是完全递归的,不需要单独的Component或Leaf对象。
通过向父节点添加节点来构建一个小型的复合树。一旦完成,我们调用traverse,它将遍历树中的每个节点,并显示其名称和深度(通过缩进显示)。日志函数用来记录和显示结果。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>组合模式:公众号AlbertYang</title>
</head>
<body>
</body>
<script>
var Node = function(name) {
this.children = [];
this.name = name;
}
Node.prototype = {
add: function(child) {
this.children.push(child);
},
remove: function(child) {
var length = this.children.length;
for (var i = 0; i < length; i++) {
if (this.children[i] === child) {
this.children.splice(i, 1);
return;
}
}
},
getChild: function(i) {
return this.children[i];
},
hasChildren: function() {
return this.children.length > 0;
}
}
// 使用递归遍历一个树
function traverse(indent, node) {
log.add(Array(indent++).join("--") + node.name);
for (var i = 0, len = node.children.length; i < len; i++) {
traverse(indent, node.getChild(i));
}
}
// 日志函数记录和打印结果
var log = (function() {
var log = "";
return {
add: function(msg) {
log += msg + "n";
},
show: function() {
console.info("%c%s", "color:red; font-size:18px", log);
log = "";
}
}
})();
function run() {
var tree = new Node("root");
var left = new Node("left")
var right = new Node("right");
var leftleft = new Node("leftleft");
var leftright = new Node("leftright");
var rightleft = new Node("rightleft");
var rightright = new Node("rightright");
tree.add(left);
tree.add(right);
tree.remove(right); // 删除节点
tree.add(right);
left.add(leftleft);
left.add(leftright);
right.add(rightleft);
right.add(rightright);
traverse(1, tree);
log.show();
}
run();
</script>
</html>
4 应用实例
4.1 表单验证
演示地址:https://www.albertyy.com/2020/8/Component1.html
表单验证中,需要做的工作是表单的保存、恢复和验证表单中的值,然而表单的数量是未知数,类型是未知数,只有功能能确定,在这种情况下,使用组合模式无疑最好,通过给每个表单添加功能,然后一个表单对象组合起来,通过操作表单对象即可达到操作表单。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>组合模式实例应用:公众号AlbertYang</title>
</head>
<body>
<input type="button" value="存储" onclick="a()" />
<input type="button" value="取出" onclick="b()" />
</body>
<script type="text/javascript">
//存储的值
var value_content = {};
function setCookie(name, value) {
value_content[name] = value;
}
function getCookie(name) {
return value_content[name];
}
//表单组合对象
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) {
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 (child == this.formComponents[i]) {
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.restore = function() {
for (var i = 0, len = this.formComponents.length; i < len; i++) {
this.formComponents[i].restore();
}
}
CompositeForm.prototype.getElement = function() {
return this.element;
}
//接口方法
var Field = function(id) {
this.id = id;
this.element;
this.content;
};
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('错误');
}
Field.prototype.restore = function() {
this.content.value = getCookie(this.id);
};
//继承方法
function extend(subClass, superClass) {
var F = function() {};
F.prototype = superClass.prototype;
subClass.prototype = new F();
subClass.prototype.constructor = subClass;
subClass.superclass = superClass.prototype;
if (superClass.prototype.constructor == Object.prototype.constructor) {
superClass.prototype.constructor = superClass;
}
}
//输入框
var InputField = function(id, label) {
Field.call(this, id);
this.input = document.createElement('input');
this.content = this.input;
this.label = document.createElement('label');
var labelTextNode = document.createTextNode(label);
this.label.appendChild(labelTextNode);
this.element = document.createElement('div');
this.element.id = id;
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.content = this.textarea;
this.label = document.createElement('label');
var labelTextNode = document.createTextNode(label);
this.label.appendChild(labelTextNode);
this.element = document.createElement('div');
this.element.id = id;
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.options.add(new Option("sfs", "sfs"));
this.select.options.add(new Option("111", "2222222222")); //这边value会改变
this.content = this.select;
this.label = document.createElement('label');
var labelTextNode = document.createTextNode(label);
this.label.appendChild(labelTextNode);
this.element = document.createElement('div');
this.element.id = id;
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.options.selectedIndex].value;
};
//表单域
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.add = function(child) {
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(id) {
if (this.components[id] != undefined) {
return this.components[id];
} else {
return null;
}
};
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 contactForm = new CompositeForm('contact-form', 'POST', 'test');
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);
var addressFieldset = new CompositeFieldset('address-fieldset');
addressFieldset.add(new InputField('address', 'Address'));
addressFieldset.add(new InputField('city', 'City'));
addressFieldset.add(new SelectField('state', 'State'));
addressFieldset.add(new InputField('zip', 'Zip'));
contactForm.add(addressFieldset);
contactForm.add(new TextareaField('comments', 'Comments'));
document.body.appendChild(contactForm.getElement());
function a() {
contactForm.save();
}
function b() {
contactForm.restore();
}
</script>
</html>
4.1 图片阅读器
演示地址:https://www.albertyy.com/2020/8/Component2.html
图片阅读器与表单验证一样,通过汇合操作图片。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>组合模式实例应用:公众号AlbertYang</title>
</head>
<body>
<input value="隐藏" type="button" onclick="a()" />
<input value="显示" type="button" onclick="b()" />
</body>
<script type="text/javascript">
//图片库
var DynamicGallery = function(id) {
this.children = [];
this.element = document.createElement('div');
this.element.id = id;
this.element.className = 'dynamic-gallery';
};
DynamicGallery.prototype = {
add: function(child) {
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(chld.getElement());
},
getChild: function(i) {
return this.children[i];
},
hide: function() {
for (var i = 0, node; node = this.getChild(i); i++) {
node.hide();
}
this.element.style.display = 'none';
},
show: function() {
this.element.style.display = 'block';
for (var i = 0, node; node = this.getChild(i); i++) {
node.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() {},
getChild: function() {},
hide: function() {
this.element.style.display = 'none';
},
show: function() {
this.element.style.display = '';
},
getElement: function() {
return this.element;
}
};
//汇合起来
var topGallery = new DynamicGallery('top-gallery');
topGallery.add(new GalleryImage('img/1.jpg'));
topGallery.add(new GalleryImage('img/2.jpg'));
topGallery.add(new GalleryImage('img/3.jpg'));
var vacationPhotos = new DynamicGallery('vacation-photos');
for (var p = 0; p < 30; p++) {
vacationPhotos.add(new GalleryImage('img/3.jpg'));
}
topGallery.add(vacationPhotos);
document.body.appendChild(topGallery.getElement());
function a() {
topGallery.hide();
}
function b() {
topGallery.show();
}
</script>
</html>
5 总结
组合模式通过简单的操作就能达到复杂的效果,一个操作通过遍历递归传递这个操作。不过组合模式的弱点也在于此,如果层次过多,则性能将受到影响。组合模式应用需要符合两个条件,一是产生递归,二是具有相同的动作。
今天的学习就到这里,你可以使用今天学习的技巧来改善一下你曾经的代码,如果想继续提高,欢迎关注我,每天学习进步一点点,就是领先的开始。
- 批量替换文件名和文本文件内容mac
- 抓取手机app的数据(摩拜单车)
- Hadoop离线数据分析平台实战——520项目总结Hadoop离线数据分析平台实战——520项目总结
- 爬虫入门到精通-mongodb的基本使用
- 拒绝撕逼,用数据来告诉你选择器到底哪家强
- 爬虫入门到精通-headers的详细讲解(If-modified-since)
- Linux配置网卡
- 【爬虫军火库】生成指定日期间的日期列表
- 手把手教你安装大数据开发测试环境手把手教你安装大数据开发测试环境
- Humble Numbers(丑数) 超详解!
- 1284 2 3 5 7的倍数
- 爬虫入门到精通-爬虫之异步加载(实战花瓣网)
- 【爬虫军火库】Chrome F12使用Network分析异步请求
- 1305 Pairwise Sum and Divide
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Nginx安装与使用
- 基于Redis实现分布式锁
- 通过简单代码示例了解七大软件设计原则
- Flink在新浪微博的在线机器学习和实时数据分析
- Nginx + Keepalived使用文档
- 22+ 高频实用的 JavaScript 片段 (2020年)
- 文件上传C:fakepath解决方案
- Asp.net web api部署在某些服务器上老是404
- Spring解决循环依赖的思路
- Spring中Bean的创建流程
- 详解RocketMQ事务消息
- 带你白嫖程序带宽限制命令Trickle
- Fix SSH客户端登录会话超时设置
- JAVA的泛型与反射的联合应用
- 专项测试实战 | 如何测试 App 流畅度(基于 FPS 和丢帧率)