1. 組合模式的定義
組合模式,將對象組合成 樹形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu),組合模式使得用戶對單個對象和組合對象的使用具有一致性。

image.png
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('打開空調(diào)');
}
};
/**********家里的電視和音響是連接在一起的,所以可以用一個宏命令來組合打開電視和打開音響的命令 *********/
const openTvCommand = {
execute: function () {
console.log('打開電視');
}
};
const openSoundCommand = {
execute: function () {
console.log('打開音響');
}
};
const macroCommand1 = MacroCommand();
macroCommand1.add(openTvCommand);
macroCommand1.add(openSoundCommand);
/*********關(guān)門、打開電腦和打登錄 QQ的命令****************/
const closeDoorCommand = {
execute: function () {
console.log('關(guān)門');
}
};
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);
/*********現(xiàn)在把所有的命令組合成一個“超級命令”**********/
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( '學(xué)習(xí)資料' );
const folder1 = new Folder( 'JavaScript' );
const folder2 = new Folder ( 'jQuery' );
const file1 = new File( 'JavaScript設(shè)計模式與開發(fā)實踐' );
const file2 = new File( '精通 jQuery' );
const file3 = new File( '重構(gòu)與模式' )
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) => {
// 先取出第一層 進(jìn)行創(chuàng)建真實dom
let { type, props } = eleObj;
let elementNode = document.createElement(type); // 創(chuàng)建第一個元素
for (let attr in props) { // 循環(huán)所有屬性
if (attr === 'children') { // 如果是children表示有嵌套關(guān)系
if (typeof props[attr] == 'object') { // 看是否是只有一個文本節(jié)點
props[attr].forEach(item => { // 多個的話循環(huán)判斷 如果是對象再次調(diào)用render方法
if (typeof item === 'object') {
render(item, elementNode)
} else { //是文本節(jié)點 直接創(chuàng)建即可
elementNode.appendChild(document.createTextNode(item));
}
})
} else { // 只有一個文本節(jié)點直接創(chuàng)建即可
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. 總結(jié)
組合模式可以讓我們使用樹形方式創(chuàng) 建對象的結(jié)構(gòu)。我們可以把相同的操作應(yīng)用在組合對象和單個對象上。在大多數(shù)情況下,我們都 可以忽略掉組合對象和單個對象之間的差別,從而用一致的方式來處理它們。
然而,組合模式并不是完美的,它可能會產(chǎn)生一個這樣的系統(tǒng):系統(tǒng)中的每個對象看起來都 與其他對象差不多。它們的區(qū)別只有在運行的時候會才會顯現(xiàn)出來,這會使代碼難以理解。此外,
如果通過組合模式創(chuàng)建了太多的對象,那么這些對象可能會讓系統(tǒng)負(fù)擔(dān)不起。