The Composite design pattern is a structural pattern useful for hierarchical management.
The Composite design pattern,
Allows you to represent individual entities(leaves) and groups of leaves as the same.
Is a structural design pattern that lets you compose objects into a changeable tree structure.
Is great if you need the option of swapping hierarchical relationships around.
Allows you to add/remove components to the hierarchy.
Provides flexibility of structure
Examples of using the Composite Design Pattern can be seen in a file system directory structure where you can swap the hierarchy of files and folders, and also in a drawing program where you can group, ungroup, transform objects and change multiple objects at the same time.
Terminology
Component Interface: The interface that all leaves and composites should implement.
Leaf: A single object that can exist inside or outside a composite.
Composite: A collection of leaves and/or other composites.
Composite UML Diagram
Source Code
In this concept code, two leaves are created, LEAF_A and LEAF_B, and two composites are created, COMPOSITE_1 and COMPOSITE_2.
LEAF_A is attached to COMPOSITE_1.
Then I change my mind and attach LEAF_A to COMPOSITE_2.
// The Composite pattern conceptinterfaceICompositeComponent{// A component interface describing the common// fields and methods of leaves and compositesname:string// A name for this componentreferenceToParent?:Compositemethod():void// A method each Leaf and composite container should implementdetach():void// Called before a leaf is attached to a composite}classLeafimplementsICompositeComponent{// A Leaf can be added to a composite, but not a leafreferenceToParent?:Composite=undefinedname:stringconstructor(name:string){this.name=name}method():void{constparent=this.referenceToParent?this.referenceToParent.name:'none'console.log(`<Leaf>\t\tname:${this.name}\tParent:\t${parent}`)}detach():void{'Detaching this leaf from its parent composite'if(this.referenceToParent){this.referenceToParent.delete(this)}}}classCompositeimplementsICompositeComponent{// A composite can contain leaves and compositesreferenceToParent?:Compositecomponents:ICompositeComponent[]name:stringconstructor(name:string){this.name=namethis.components=[]}method():void{constparent=this.referenceToParent?this.referenceToParent.name:'none'console.log(`<Composite>\tname:${this.name}\tParent:\t${parent}\tComponents:${this.components.length}`)this.components.forEach((component)=>{component.method()})}attach(component:ICompositeComponent):void{// Detach leaf / composite from any current parent reference and// then set the parent reference to this compositecomponent.detach()component.referenceToParent=thisthis.components.push(component)}delete(component:ICompositeComponent):void{// Removes leaf/composite from this composite this.componentsconstindex=this.components.indexOf(component)if(index>-1){this.components.splice(index,1)}}detach():void{// Detaching this composite from its parent compositeif(this.referenceToParent){this.referenceToParent.delete(this)this.referenceToParent=undefined}}}// The ClientconstLEAF_A=newLeaf('leaf-a')constLEAF_B=newLeaf('leaf-b')constCOMPOSITE_1=newComposite('comp-1')constCOMPOSITE_2=newComposite('comp-2')// Attach LEAF_A to COMPOSITE_1COMPOSITE_1.attach(LEAF_A)// Instead, attach LEAF_A to COMPOSITE_2COMPOSITE_2.attach(LEAF_A)// Attach COMPOSITE1 to COMPOSITE_2COMPOSITE_2.attach(COMPOSITE_1)// Run the methods thatLEAF_B.method()// not in any compositesCOMPOSITE_2.method()// COMPOSITE_2 contains both COMPOSITE_1 and LEAF_A
Demonstration of a simple in memory hierarchical file system.
A root object is created that is a composite.
Several files (leaves) are created and added to the root folder.
More folders (composites) are created, and more files are added, and then the hierarchy is reordered.
Composite Example UML Diagram
Source Code
./src/composite/client.ts
1 2 3 4 5 6 7 8 9101112131415161718192021222324
// A use case of the composite pattern.importFilefrom'./file'importFolderfrom'./folder'constFILESYSTEM=newFolder('root')constFILE_1=newFile('abc.txt')constFILE_2=newFile('123.txt')FILESYSTEM.attach(FILE_1)FILESYSTEM.attach(FILE_2)constFOLDER_A=newFolder('folder_a')FILESYSTEM.attach(FOLDER_A)constFILE_3=newFile('xyz.txt')FOLDER_A.attach(FILE_3)constFOLDER_B=newFolder('folder_b')constFILE_4=newFile('456.txt')FOLDER_B.attach(FILE_4)FILESYSTEM.attach(FOLDER_B)FILESYSTEM.dir('')// now move FOLDER_A and its contents to FOLDER_Bconsole.log()FOLDER_B.attach(FOLDER_A)FILESYSTEM.dir('')
./src/composite/file.ts
1 2 3 4 5 6 7 8 9101112131415161718192021222324
importIComponentfrom'./icomponent'importFolderfrom'./folder'exportdefaultclassFileimplementsIComponent{// The File Class. The files are the leavesname:stringreferenceToParent?:Folder=undefinedconstructor(name:string){this.name=name}dir(indent:string):void{console.log(`${indent}<FILE> ${this.name}`)}detach():void{'Detaching this leaf from its parent composite'if(this.referenceToParent){this.referenceToParent.delete(this)}}}
importIComponentfrom'./icomponent'exportdefaultclassFolderimplementsIComponent{// A composite can contain leaves and compositesreferenceToParent?:Foldername:stringcomponents:IComponent[]constructor(name:string){this.name=namethis.components=[]}dir(indent:string):void{console.log(`${indent}<DIR> ${this.name}`)this.components.forEach((component)=>{component.dir(indent+'..')})}attach(component:IComponent):void{// Detach leaf / composite from any current parent reference and// then set the parent reference to this compositecomponent.detach()component.referenceToParent=thisthis.components.push(component)}delete(component:IComponent):void{// Removes leaf/composite from this composite this.componentsconstindex=this.components.indexOf(component)if(index>-1){this.components.splice(index,1)}}detach():void{// Detaching this composite from its parent compositeif(this.referenceToParent){this.referenceToParent.delete(this)this.referenceToParent=undefined}}}
./src/composite/icomponent.ts
1 2 3 4 5 6 7 8 91011121314
importFolderfrom'./folder'exportdefaultinterfaceIComponent{// A component interface describing the common// fields and methods of leaves and compositesreferenceToParent?:Folderdir(indent:string):void// A method each Leaf and composite container should implementdetach():void// Called before a leaf is attached to a composite}
The Composite design pattern allows you to structure components in a manageable hierarchical order.
It provides flexibility of structure since you can add/remove and reorder components.
File explorer on Windows is a very good example of the composite design pattern in use.
Any system where you need to offer at runtime the ability to group, ungroup, modify multiple objects at the same time, would benefit from the composite design pattern structure. Programs that allow you to draw shapes and graphics will often also use this structure as well.