1. 组合模式的定义
组合模式,将对象组合成 树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。
2. 场景
2.1 宏命令
<html>
<body>
<button id="button">按我</button></body>
<script>
const MacroCommand = function () {
return {
commandsList: [],
add: function (command) {
this.commandsList.push(command);
},
execute: function () {
for (let i = 0, command; command = this.commandsList[i++];) {
command.execute();
}
}
}
};
const openAcCommand = {
execute: function () {
console.log('打开空调');
}
};
/**********家里的电视和音响是连接在一起的,所以可以用一个宏命令来组合打开电视和打开音响的命令 *********/
const openTvCommand = {
execute: function () {
console.log('打开电视');
}
};
const openSoundCommand = {
execute: function () {
console.log('打开音响');
}
};
const macroCommand1 = MacroCommand();
macroCommand1.add(openTvCommand);
macroCommand1.add(openSoundCommand);
/*********关门、打开电脑和打登录 QQ的命令****************/
const closeDoorCommand = {
execute: function () {
console.log('关门');
}
};
const openPcCommand = {
execute: function () {
console.log('开电脑');
}
};
const openQQCommand = {
execute: function () {
console.log('登录 QQ');
}
};
const macroCommand2 = MacroCommand();
macroCommand2.add(closeDoorCommand);
macroCommand2.add(openPcCommand);
macroCommand2.add(openQQCommand);
/*********现在把所有的命令组合成一个“超级命令”**********/
const macroCommand = MacroCommand();
macroCommand.add(openAcCommand);
macroCommand.add(macroCommand1);
macroCommand.add(macroCommand2);
/*********最后给遥控器绑定“超级命令”**********/
const setCommand = (function (command) {
document.getElementById('button').onclick = function () {
command.execute();
}
})(macroCommand);
</script>
</html>
2.2 扫描文件夹
/******************************* Folder ******************************/
const Folder = function( name ){
this.name = name;
this.files = [];
};
Folder.prototype.add = function( file ){
this.files.push( file );
};
Folder.prototype.scan = function(){
console.log( '开始扫描文件夹: ' + this.name );
for ( let i = 0, file, files = this.files; file = files[ i++ ]; ){
file.scan();
}
};
/******************************* File ******************************/
const File = function( name ){
this.name = name;
};
File.prototype.add = function(){
throw new Error( '文件下面不能再添加文件' );
};
File.prototype.scan = function(){
console.log( '开始扫描文件: ' + this.name );
};
const folder = new Folder( '学习资料' );
const folder1 = new Folder( 'JavaScript' );
const folder2 = new Folder ( 'jQuery' );
const file1 = new File( 'JavaScript设计模式与开发实践' );
const file2 = new File( '精通 jQuery' );
const file3 = new File( '重构与模式' )
folder1.add( file1 ); folder2.add( file2 );
folder.add( folder1 ); folder.add( folder2 ); folder.add( file3 );
const folder3 = new Folder( 'Nodejs' );
const file4 = new File( '深入浅出 Node.js' );
folder3.add( file4 );
const file5 = new File( 'JavaScript语言精髓与编程实践' );
folder.add( folder3 );
folder.add( file5 );
folder.scan();
2.3 react
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
<style>
.red {
color: red;
}
</style>
</head>
<body>
<div id="root"></div>
<script>
function ReactElement(type, props) {
this.type = type;
this.props = props;
}
let React = {
createElement(type, props = {}, ...childrens) {
childrens.length === 1 ? childrens = childrens[0] : void 0
return new ReactElement(type, { ...props, children: childrens })
}
};
let render = (eleObj, container) => {
// 先取出第一层 进行创建真实dom
let { type, props } = eleObj;
let elementNode = document.createElement(type); // 创建第一个元素
for (let attr in props) { // 循环所有属性
if (attr === 'children') { // 如果是children表示有嵌套关系
if (typeof props[attr] == 'object') { // 看是否是只有一个文本节点
props[attr].forEach(item => { // 多个的话循环判断 如果是对象再次调用render方法
if (typeof item === 'object') {
render(item, elementNode)
} else { //是文本节点 直接创建即可
elementNode.appendChild(document.createTextNode(item));
}
})
} else { // 只有一个文本节点直接创建即可
elementNode.appendChild(document.createTextNode(props[attr]));
}
} else if (attr === 'className') { // 是不是class属性 class 属性特殊处理
elementNode.setAttribute('class', props[attr]);
} else {
elementNode.setAttribute(attr, props[attr]);
}
}
container.appendChild(elementNode)
};
//ReactDOM.render(<div>hello<span>world</span></div>);
//ReactDOM.render(React.createElement("div",null,"hello,",React.createElement("span",null,"world")));
render(React.createElement("div", null, "hello,", React.createElement("span", { class: "red" }, "world")), document.getElementById('root'));
</script>
</body>
</html>
3. 总结
组合模式可以让我们使用树形方式创 建对象的结构。我们可以把相同的操作应用在组合对象和单个对象上。在大多数情况下,我们都 可以忽略掉组合对象和单个对象之间的差别,从而用一致的方式来处理它们。
然而,组合模式并不是完美的,它可能会产生一个这样的系统:系统中的每个对象看起来都 与其他对象差不多。它们的区别只有在运行的时候会才会显现出来,这会使代码难以理解。此外,
如果通过组合模式创建了太多的对象,那么这些对象可能会让系统负担不起。