Definition 1.

Composition pattern: Use small children to build larger objects, which themselves may be made up of smaller “grandchildren”.

2. Review macro commands

A macro command object consists of a specific set of subcommand objects. In both cases, a execute method is used to execute the command.

var closeDoorCommand = {
    execute: function(){
        console.lgo('shut down')}}var openPcCommand = {
    execute: function(){
        console.lgo('Turn on the computer')}}var openQQCommand = {
    execute: function(){
        console.lgo('login QQ')}}var MacroCommand = function(){
    return {
        commandsList: [].add: function(command){
            this.commandsList.push(command)
        },
        execute: function(){
            for(var i=0, command; command=this.commandsList[i++];) { command.execute(); }}}}var macroCommand = MacroCommand();
macroCommand.add(closeDoorCommand)
macroCommand.add(openPcCommand)
macroCommand.add(openQQCommand)

macroCommand.execute();
Copy the code

A macro command contains a set of subcommands that form a tree structure, a very simple tree structure

MacroCommand is called composite object, closeDoorCommand, openPcCommand, openQQCommand are leaf objects. In macroCommand’s execute method, the actual operation is not performed. Instead, it iterates through the leaf objects it contains, delegating the actual execute request to them.

MacroCommand behaves like a command, but it’s really just a “proxy” for a set of real commands. Not really a proxy. Although structurally similar, macroCommand is only responsible for passing requests to leaf objects. Its purpose is not to control access to leaf objects.

3. The use of composite patterns

The composition pattern groups objects into a tree structure to represent a partial-whole hierarchy. In addition to being used to represent tree structures, another benefit of the composite pattern is that it enables consistent use of individual and composite objects through the polymorphic representation of objects.

  • Represents a tree structure. Provides a scheme to traverse the tree structure. By calling the execute method of the composite object, the program recursively calls the execute method of the leaf object below the composite object. The composite pattern is a very convenient way to describe object partial-whole hierarchies

  • Object polymorphism is used to treat combined objects and single objects uniformly. The polymorphic representation of an object enables clients to ignore the differences between a composite object and a single object. In the composite pattern, the customer uses all objects in the composite structure uniformly, regardless of whether it is a composite object or a single object.

4. The process by which requests are passed through the tree

In the case of a macro command, requests are passed down from the object at the top of the tree. If the object currently processing the request is a leaf object (a common subcommand), the leaf itself will process the request accordingly. If the object currently processing the request is a composite object (macro command), the composite object iterates through its children, passing the request on to those children.

In summary, if the child node is a leaf object, the leaf object itself handles the request, whereas if the child node is still a composite object, the request is passed down. There are no more children under a leaf object, a leaf object is the end of the leaf of a number, and there may be children under a composite object.

Requests are passed up and down the tree to the end of the tree. As a customer, you only need to care about the composite object at the top of the tree, you just need to request this composite object, and the request will be passed down the tree to all the leaf objects.

Iterators work if you simply iterate over a set of child nodes.

5. More powerful macro commands

var MacroCommand = function(){
    return {
        commandsList: [].add: function(command){
            this.commandsList.push(command)
        },
        execute: function(){
            for(var i=0, command; command=this.commandsList[i++];) { command.execute(); }}}}var openAcCommand = {
    execute: function(){
        console.log('Turn on the air conditioner')}}var openTvCommand = {
    execute: function(){
        console.log('Turn on the TV')}}var openSoundCommand = {
    execute: function(){
        console.log('Turn on the stereo')}}var macroCommand1 = MacroCommand();
macroCommand1.add(openTvCommand);
macroCommand1.add(openSoundCommand);

var closeDoorCommand = {
    execute: function(){
        console.log('shut down')}}var openPcCommand = {
    execute: function(){
        console.log('Turn on the computer')}}var openQQCommand = {
    execute: function(){
        console.log('login QQ')}}var macroCommand2 = MacroCommand();
macroCommand2.add(closeDoorCommand);
macroCommand2.add(openPcCommand);
macroCommand2.add(openQQCommand);

var macroCommand = MacroCommand();
macroCommand.add(openAcCommand)
macroCommand.add(macroCommand1)
macroCommand.add(macroCommand2)

var setCommand = (function(command){
    document.getElementById('button').onclick = function(){
        command.execute();
    }
})(macroCommand)
Copy the code

Base objects can be combined into more complex composite objects, which can be combined into more complex composite objects, and so on recursively, and the structure of the tree can support any amount of complexity. After the tree is finally constructed, the steps to get the entire tree to finally work are as simple as the execute method on the topmost object. Every time a request is made to the topmost object, it is actually a depth-first search of the entire tree, and the programmer who created the composite object does not care about the inherent details. It is very easy to add some new node objects to the tree.

6. Transparency brings security issues

The transparency of the composite pattern allows the requesting client to ignore the distinction between composite and leaf objects in the tree, but they are fundamentally different

Composite objects can have child nodes, but leaf objects have no child nodes under them, so you might do something wrong, such as try to add child nodes to leaf objects. The usual solution is to add an Add method to the leaf object and, when called, throw an exception to alert the client in time

var MacroCommand = function(){
    return {
        commandsList: [].add: function(command){
            this.commandsList.push(command)
        },
        execute: function(){
            for(var i=0, command; command=this.commandsList[i++];) { command.execute(); }}}}var openTvCommand = {
    execute: function(){
        console.log('Turn on the TV')},add: function(){
        throw new Error('Leaf object cannot add child nodes')}}Copy the code

7. Example of composite mode – Scan folders

The relationship between folders and files. It is well suited to be described in a composite pattern. Folders can contain both files and other folders, eventually forming a tree.

// Folder Folder and File File
var 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(var i=0, file, files = this.files; file = files[i++];) { file.scan(); }}var File = function(name){
    this.name = name;
}

File.prototype.add = function(){
    throw new Error('File cannot be added under file');
}

File.prototype.scan = function(){
    console.log('Start scanning files :' + this.name)
}

var folder = new Folder('Learning Materials');
var folder1 = new Folder('JavaScript');
var folder2 = new Folder('jQuery');

var file1 = new File('JavaScript Design Patterns and Development Practices');
var file2 = new File('master of jQuery');var file3 = new File('Refactoring and Patterns');

folder1.add(file1);
folder2.add(file2);

folder.add(folder1)
folder.add(folder2)
folder.add(file3)
Copy the code

Treat composite objects and leaf objects equally.

Change the tree structure, add new data, but do not modify any of the original code, in line with the open and closed principle.

8. Attention points

    1. Combinatorial patterns are not parent-child relationships

The tree structure of the composite pattern is easily mistaken for the parent-child relationship between the composite object and the leaf object, which is incorrect

The composition pattern IS A has-A (aggregation) relationship, not IS-A. A composite object contains a set of leaf objects. A composite object delegates requests to all the leaf objects it contains, all of which have the same interface.

    1. Consistency of operations on leaf objects

In addition to the requirement that the composite object and the leaf object have the same interface, a necessary condition is that the operation on a group of leaf objects must be consistent

    1. Bidirectional mapping

Bidirectional mapping of parent and child nodes allows the introduction of an intermediary pattern to manage these objects

    1. Using responsibility chain pattern to improve composite pattern performance

In the composite mode, if the structure of the tree is complex and the number of nodes is large, the performance may not be ideal when traversing the tree. There are a few tricks you can use to avoid traversing the entire tree in practice. One off-the-shelf solution is to use the chain of responsibilities pattern. Responsibility chain mode usually requires manually setting the chain, but in composite mode, there is actually a natural responsibility chain between parent and child objects. Passing requests along the chain from parent to child, or vice versa, until an object can handle the request is one of the classic uses of the chain of responsibility pattern

9. Reference the parent object

A composite object holds a reference to the child nodes below it, which is characteristic of the composite pattern, where the tree structure is top-down. But sometimes we need to keep a reference to the parent on the child, such as when a chain of responsibility is used in the composite pattern, which may require requests to bubble from the child to the parent. When we delete a file, we actually delete the file from the upper folder of the file

Rewrite the Folder and File classes by adding the this.parent property to the constructors of both classes and setting the correct parent node of the File or Folder when calling add:

 var Folder = function(name) {
    this.name = name;
    this.parent = null;
    this.files = [];
}

Folder.prototype.add = function(file) {
    this.parent = this;
    this.files.push(file);
}

Folder.prototype.scan = function(){
    console.log('Start scanning folders :' + this.name);
    for(var i=0, file, files = this.files; file = files[i++];) {
        file.scan();
    }
}

Folder.prototype.remove = function(){
    if (!this.parent) {
        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); }}}Copy the code

If this.parent is null, then the Folder is either the root node of the tree or has not yet been added to the free node of the tree. No node needs to be removed from the tree. For the moment, remove will return without doing anything

If this.parent is not null, it indicates that the folder has a parent node. In this case, the list of child nodes saved in the parent node is iterated and the child node that you want to delete is deleted

var File = function(name){
    this.name = name;
    this.parent = null;
}

File.prototype.add = function(){
    throw new Error('File cannot be added under file');
}

File.prototype.scan = function(){
    console.log('Start scanning files :' + this.name)
}

 File.prototype.remove = function(){
    if (!this.parent) {
        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); }}}Copy the code

10. When to use composite mode

When used properly, the composite pattern can greatly simplify the customer’s code. The following applies

    1. Represents a partial-whole hierarchy of objects. The composite pattern makes it easy to construct a tree to represent the partial-global structure of an object. This is especially true during development when it is uncertain how many levels the tree actually exists. After the construction of the tree is finally complete, operations can be performed uniformly on the entire tree simply by requesting the topmost object of the tree. Adding and removing nodes in a tree in composite mode is convenient and follows the open-closed principle
    1. The customer wants to treat all objects in the tree uniformly. The composite mode allows the customer to ignore the distinction between composite objects and leaf objects. The customer does not care whether the object being processed is a composite object or a leaf object when facing the tree, and does not have to write a bunch of if and else statements to process them separately. Composite objects and leaf objects each do their own right thing, which is the most important capability of composite patterns

11. A summary

The composite pattern allows us to create the structure of an object in a tree fashion. The same operations can be applied to composite objects as well as to individual objects. In most cases, we can ignore the differences between composite objects and individual objects and treat them in a consistent way

However, the combined pattern is not perfect and can result in a system where every object in the system looks pretty much like every other object. The difference is only apparent at run time, which makes the code hard to understand. In addition, if too many objects are created through the composite pattern, they may become unaffordable for the system