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

Take the macro command code in command mode as an example. A macro command object contains a specific set of subcommand objects. Both the macro command object and the subcommand object have a execute method responsible for executing the command. A macro command contains a set of subcommands that form a tree structure. Here is a very simple tree

In composite mode, requests always follow a logic as they pass through the tree. 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 object 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.

  • Macro command in combination modeThe current universal remote control, including close the door, open the computer, login QQ these three commands. Now we need a “super universal remote control” that can control all the appliances in our home. This remote control has the following functions
    • Turn on the air conditioning
    • Turn on the TV and stereo
    • Close the door, open the computer, log in 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 openAcCommand = { execute:function(){
        console.log( 'Turn on the air conditioner'); }}; /* openTvCommand = {execute: /* 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); /* 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 ); */ macroCommand = macroCommand (); macroCommand.add( openAcCommand ); macroCommand.add( macroCommand1 ); macroCommand.add( macroCommand2 ); /* Finally bind "super command" */ var for remote controlsetCommand = (function( command ){ 
    document.getElementById( 'button' ).onclick = function(){
        command.execute(); 
    }
})( macroCommand );
Copy the code

As you can see from this example, basic objects can be composed into more complex composite objects, which can be composed again, and so on recursively, and the structure of the tree can support any amount of complexity. Once the tree is finally constructed, the process of getting the whole tree to finally work is as simple as calling 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.

Application scenario: Scanning folders

The relationship between folders and files is best described in the composite pattern. Folders can contain both files and other folders, which may eventually be combined into a tree. When scanning this folder with anti-virus software, we often do not care how many files and subfolders are inside. The combined mode allows us to scan only the outermost folder.

Code implementation

/* Folder */ 
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++ ]; ){
        files file.scan();
    } 
};
/*File*/
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 ); }; /* Create some folders and file objects and combine them into a tree. This tree is our existing file directory structure in F */ 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 ); */ var folder3 = new Folder() */ var folder3 = new Folder()'Nodejs' );
var file4 = new File( 'Node.js' ); 
folder3.add( file4 );
var file5 = new File( 'JavaScript Language essence and Programming Practices'); /* Add these files to the existing tree */ folder.add(folder3); folder.add( file5 );Copy the code

summary

The composite pattern allows us to create the structure of an object in a tree fashion. We can apply the same operations to composite objects as to individual objects. In most cases, we can ignore the difference between composite objects and individual objects and treat them in a consistent way. However, the composite pattern is not perfect, and it 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.

Series of articles:

JavaScript Design Patterns and Development Practices