组合模式,又叫整体-部分模式,它允许你将对象组合成树形结构来表现
整体-部分层次结构,让使用者可以以一致的方式处理组合对象以及部分对象。
例如树形文件目录结构,就符合组合模式的特征
用组合模式实现,以下场景:
本地有一个【学习资料】文件夹,文件夹下有两个文件夹【大桥老师】和【小苍老师】,
两文件夹下分别各自有一些资料和文件夹,我们要在这些资料文件夹里找到大于2G的资料文件,
并输出文件名和大小。
/*
* @Author: HotSuitor
* @Date: 2020-03-18 17:14:19
* @LastEditors: hs
* @LastEditTime: 2020-03-18 18:33:35
* @Description: hotsuitor@qq.com
*/
// 创建文件夹
var createFolder = function(name) {
return {
name: name,
_children: [],
// 在文件夹下创建文件夹或文件
add: function(fileOfFolder) {
this._children.push(fileOfFolder);
},
//扫描方法
scan: function(cb) {
this._children.forEach(function(child) {
child.scan(cb);
});
}
};
};
// 创建文件
var createFile = function(name, size) {
return {
name: name,
size: size,
//
add: function() {
throw new Error("文件下面不能创建文件");
},
scan: function(cb) {
cb(this);
}
};
};
var foldMovies = createFolder("学习资料");
// 创建子文件夹,并放入根文件夹
var foldMovieDaQiao = createFolder("大桥老师");
foldMovies.add(foldMovieDaQiao);
var foldMovieXiaoCan = createFolder("小苍老师");
foldMovies.add(foldMovieXiaoCan);
// 添加学习资料
foldMovieDaQiao.add(createFile("如何骑马.avi", 1.2));
foldMovieDaQiao.add(createFile("优雅舞姿.avi", 1.7));
foldMovieDaQiao.add(createFile("射箭五十练.avi", 2.4));
foldMovieDaQiao.add(createFile("优雅喝酒.avi", 2.1));
foldMovieXiaoCan.add(createFile("海边如何防晒.mp6", 1.8));
foldMovieXiaoCan.add(createFile("如何布置房间.mp6", 1.5));
foldMovieXiaoCan.add(createFile("窗帘的设计.mp6", 2.7));
console.log("大于2G的学习资料有:");
foldMovies.scan(function(item) {
if (item.size > 2) {
console.log("name:" + item.name + " size:" + item.size);
}
});
之前学过链模式,可以用链模式改造,简洁代码
// 链式改造
var createFolder = function(name) {
return {
name: name,
_children: [],
add: function(fileOfFolder) {
this._children.push(fileOfFolder);
return this;
},
scan: function(cb) {
this._children.forEach(child => {
child.scan(cb);
});
}
};
};
var createFile = function(name, size) {
return {
name: name,
size: size,
add() {
throw new Error("文件下不能创建文件");
},
scan(cb) {
cb(this);
}
};
};
var foldMovies2 = createFolder("电影")
.add(
createFolder("漫威")
.add(createFile("钢铁侠.mp4", 1.9))
.add(createFile("蜘蛛侠.mp4", 2.1))
.add(createFile("金刚狼.mp4", 2.3))
.add(createFile("美国队长.mp4", 1.4))
)
.add(
createFolder("DC电影")
.add(createFile("蝙蝠侠.mp4", 2.4))
.add(createFile("超人.mp4", 1.8))
);
console.log("\n大于1.5G的学习资料有:");
foldMovies2.scan(function(item) {
if (item.size > 1.5) {
console.log("name:" + item.name + " size:" + item.size);
}
});
实战,借助组合模式,实现dom树节点的创建
const createElement = function({ tag, attr, children }) {
const node = tag
? document.createElement(tag)
: document.createTextNode(attr.text);
tag && Object.keys(attr).forEach(key => node.setAttribute(key, attr[key]));
children &&
children.forEach(child =>
node.appendChild(createElement(child))
);
return node;
};
const ulElement = createElement({
tag: 'ul',
attr: {id: 'data-list'},
children: [
{
tag: 'li',
attr: {class: 'item'},
children:[{attr:{text: 'item1'}}]
},
{
tag: 'li',
attr: {class: 'item'},
children:[{attr:{text: 'item2'}}]
},
{
tag: 'li',
attr: {class: 'item'},
children:[{attr:{text: 'item3'}}]
},
]
})
console.log(ulElement);
document.body.appendChild(ulElement);
/* <ul id="data-list"><li class="item">item1</li><li class="item">item2</li><li class="item">item3</li></ul> */
vue和react的虚拟DOM也是这样类似的树形结构渲染DOM节点的
优缺点
优点:
- 由于组合对象和叶对象具有相同的接口,因此调用组合对象还是叶对象对使用者来说没有区别,使得使用者面向对象接口编程
- 对扩展友好,如何开闭原则,利于维护。如果想要在组合模式中增加一个节点比较容易,在目标组合节点中添加即可,不会影响到其他对象。
缺点: - 增加了系统的复杂性,如果树中的对象不多,不一定需要使用;
- 如果通过组合对象创建了太多对象,会增加系统负担。
适用场景
- 对象组织呈树形结构
- 使用者希望统一对待树形结构中的对象,比如用户不想写一堆
if-else
来处理树中的节点时