Design pattern - composite pattern
The composite pattern allows us to create the structure of objects in a tree.
We can apply the same operation to composite objects and single objects.
In most cases, we can ignore the difference between composite objects and single objects,
To deal with them in a consistent way.
Common scenario: traversing the tree. The relationship between tree and cotyledon is the application of a classic combination mode + responsibility chain mode.
Applies to both of the following situations:
- Represents the part - > overall hierarchy of an object. It is suitable when we are not sure how many levels there are in the tree during development.
After the construction of the tree is finally completed, the whole tree can be operated uniformly only by requesting the top-level object of the tree.
It is very convenient to add and delete the nodes of the tree in the combination mode, and it conforms to the open closed principle. - The customer wants to treat all objects in the tree uniformly. The composite mode allows customers to ignore the difference between composite objects and leaf objects,
When facing this tree, the customer doesn't care whether the object being processed is a composite object or a leaf object,
You don't have to write a bunch of if and else statements to deal with them separately.
Composite objects and leaf objects will do their own right things, which is the most important ability of composite mode.
Disadvantages:
3. The code is difficult to understand
4. Creating too many objects increases the burden of the system
The main switch controls household appliances.
// Super remote control // Turn on the air conditioner // Turn on the TV and audio // Close the door, turn on the computer and log in to qq let MacroCommand = () => { return { commandList: [], add(command) { this.commandList.push(command); }, execute() { for (let i = 0, command; (command = this.commandList[i++]); ) { command.execute(); } }, }; }; let openAcCommand = { execute() { console.log("Turn on the air conditioner"); }, }; let openTvCommand = { execute() { console.log("open TV"); }, }; let openSoundCommand = { execute() { console.log("Turn on the audio"); }, }; // First small combination let macroCommand1 = MacroCommand(); macroCommand1.add(openTvCommand); macroCommand1.add(openSoundCommand); let closeDoorCommand = { execute() { console.log("close"); }, }; let openPcCommand = { execute() { console.log("computer"); }, }; let openQQCommand = { execute() { console.log("qq"); }, }; // Second small combination let macroCommand2 = MacroCommand(); macroCommand2.add(closeDoorCommand); macroCommand2.add(openPcCommand); macroCommand2.add(openQQCommand); // Final large combination // Store the sub command combination into a new array to form a larger command combination // When executing, take out a sub command combination and execute, then enter the execute of executing the sub command. // At this time, a top-down node command mode will be formed to execute the combination of sub commands at all levels and spread to execute. This is called depth first search. let macroCommand = MacroCommand(); macroCommand.add(openAcCommand); macroCommand.add(macroCommand1); macroCommand.add(macroCommand2); // Bound to button, master switch. let setCommand = (function (command) { document.getElementById("button").onclick = function () { command.execute(); }; })(macroCommand)
Composite objects and leaf objects
Composite objects can have child nodes
The leaf object has no child nodes. You need to add corresponding methods to it and throw an exception to remind the user.
A combination pattern is used to describe the relationship between folders and files.
Copy folder scene, anti-virus scene
// 1. Folder combination object let Folder = function (name) { this.name = name; this.files = []; }; Folder.prototype.add = function (file) { this.files.push(file); }; Folder.prototype.scan = function () { console.log('Start scanning folders:', this.name) for (let i = 0, file, files = this.files; (file = files[i++]); ) { file.scan(); } }; // 2. File leaf object let File = function (name) { this.name = name; }; File.prototype.add = function (file) { throw Error("No more files can be added under the file"); }; File.prototype.scan = function () { console.log("Start scanning files:", this.name); }; // 3. Copy /* Source file source Learning materials -- js -- js Design pattern -- jq -- Proficient in jq restructure */ let folder = new Folder("Learning materials"); let folder1 = new Folder("js"); let folder2 = new Folder("jq"); let file1 = new File("js Design pattern"); let file2 = new File("master jq"); let file3 = new File("restructure"); folder1.add(file1); folder2.add(file2); folder.add(folder1); folder.add(folder2); folder.add(file3); /* Destination file dist nodejs -- NodeJs in simple terms -- js Language essence and programming practice */ let folder3 = new Folder("Nodejs"); let file4 = new File("explain profound theories in simple language NodeJs"); folder3.add(file4); let file5 = new File("js Language essence and programming practice"); // copy folder.add(folder3); folder.add(file5); console.log(folder) // 4. Implementation folder.scan();
distinguish:
- The combination mode is a collection relationship, not a parent-child relationship
- Leaf objects must have operational consistency
- Bidirectional mapping relationship, 1:1 relationship, not 1:n relationship. The mediator pattern can be used to solve this problem.
Use the responsibility chain model to improve the performance of the composite model
Reference the parent object to record the current file. When deleting, judge whether to delete according to this.
The functions of referencing and deleting folders of parent nodes are added
// folder let Folder = function (name) { this.name = name; this.files = []; this.parent = null; }; Folder.prototype.add = function (file) { file.parent = this; // For all added folders or files, this is bound to this in the current directory. this.files.push(file); // And loaded the instance into the files array. }; Folder.prototype.scan = function () { console.log("Start scanning folders:", this.name); for (let i = 0, file, files = this.files; (file = files[i++]); ) { file.scan(); } }; // Remove and operate this.files through this.parent Folder.prototype.remove = function () { if (!this.parent) { //Root node or free node outside the tree return; } for (var files = this.parent.files, l = files.length - 1; l >= 0; l--) { var file = files[l]; if (file === this) { files.splice(l, 1); } } }; // file let File = function (name) { this.parent = null; this.name = name; }; File.prototype.add = function (file) { throw Error("No more files can be added under the file"); }; File.prototype.scan = function () { console.log("Start scanning files:", this.name); }; File.prototype.remove = function () { if (!this.parent) return; for (let files = this.parent.files, l = files.length - 1; l >= 0; l--) { let file = files[l]; if (file === this) { files.splice(l, 1); } } }; // copy /* Learning materials -- JavaScript ((removed) -- JavaScript Design pattern and development practice -- Simple Node.js */ var folder = new Folder("Learning materials"); var folder1 = new Folder("JavaScript"); var file1 = new Folder("explain profound theories in simple language Node.js"); folder1.add(new File("JavaScript Design pattern and development practice")); folder.add(folder1); folder.add(file1); folder1.remove(); //Remove Folder folder.scan();