Composite Design Pattern
Video Lecture
Overview
... To read hidden text, either pause Video Lectures, refer to Book or subscribe to Medium Membership.
Terminology
... To read hidden text, either pause Video Lectures, refer to Book or subscribe to Medium Membership.
Composite UML Diagram

Source Code
... To read hidden text, either pause Video Lectures, refer to Book or subscribe to Medium Membership.
./src/composite/composite-concept.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101 | // The Composite pattern concept
interface ICompositeComponent {
// A component interface describing the common
// fields and methods of leaves and composites
name: string // A name for this component
referenceToParent?: Composite
method(): void // A method each Leaf and composite container should implement
detach(): void // Called before a leaf is attached to a composite
}
class Leaf implements ICompositeComponent {
// A Leaf can be added to a composite, but not a leaf
referenceToParent?: Composite = undefined
name: string
constructor(name: string) {
this.name = name
}
method(): void {
const parent = 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)
}
}
}
class Composite implements ICompositeComponent {
// A composite can contain leaves and composites
referenceToParent?: Composite
components: ICompositeComponent[]
name: string
constructor(name: string) {
this.name = name
this.components = []
}
method(): void {
const parent = 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 composite
component.detach()
component.referenceToParent = this
this.components.push(component)
}
delete(component: ICompositeComponent): void {
// Removes leaf/composite from this composite this.components
const index = this.components.indexOf(component)
if (index > -1) {
this.components.splice(index, 1)
}
}
detach(): void {
// Detaching this composite from its parent composite
if (this.referenceToParent) {
this.referenceToParent.delete(this)
this.referenceToParent = undefined
}
}
}
// The Client
const LEAF_A = new Leaf('leaf-a')
const LEAF_B = new Leaf('leaf-b')
const COMPOSITE_1 = new Composite('comp-1')
const COMPOSITE_2 = new Composite('comp-2')
// Attach LEAF_A to COMPOSITE_1
COMPOSITE_1.attach(LEAF_A)
// Instead, attach LEAF_A to COMPOSITE_2
COMPOSITE_2.attach(LEAF_A)
// Attach COMPOSITE1 to COMPOSITE_2
COMPOSITE_2.attach(COMPOSITE_1)
// Run the methods that
LEAF_B.method() // not in any composites
COMPOSITE_2.method() // COMPOSITE_2 contains both COMPOSITE_1 and LEAF_A
|
Output
node ./dist/composite/composite-concept.js
<Leaf> name:leaf-b Parent: none
<Composite> name:comp-2 Parent: none Components:2
<Leaf> name:leaf-a Parent: comp-2
<Composite> name:comp-1 Parent: comp-2 Components:0
Composite Use Case
... To read hidden text, either pause Video Lectures, refer to Book or subscribe to Medium Membership.
Composite Example UML Diagram

Source Code
./src/composite/client.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | // A use case of the composite pattern.
import File from './file'
import Folder from './folder'
const FILESYSTEM = new Folder('root')
const FILE_1 = new File('abc.txt')
const FILE_2 = new File('123.txt')
FILESYSTEM.attach(FILE_1)
FILESYSTEM.attach(FILE_2)
const FOLDER_A = new Folder('folder_a')
FILESYSTEM.attach(FOLDER_A)
const FILE_3 = new File('xyz.txt')
FOLDER_A.attach(FILE_3)
const FOLDER_B = new Folder('folder_b')
const FILE_4 = new File('456.txt')
FOLDER_B.attach(FILE_4)
FILESYSTEM.attach(FOLDER_B)
FILESYSTEM.dir('')
// now move FOLDER_A and its contents to FOLDER_B
console.log()
FOLDER_B.attach(FOLDER_A)
FILESYSTEM.dir('')
|
./src/composite/file.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | import IComponent from './icomponent'
import Folder from './folder'
export default class File implements IComponent {
// The File Class. The files are the leaves
name: string
referenceToParent?: Folder = undefined
constructor(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)
}
}
}
|
./src/composite/folder.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 | import IComponent from './icomponent'
export default class Folder implements IComponent {
// A composite can contain leaves and composites
referenceToParent?: Folder
name: string
components: IComponent[]
constructor(name: string) {
this.name = name
this.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 composite
component.detach()
component.referenceToParent = this
this.components.push(component)
}
delete(component: IComponent): void {
// Removes leaf/composite from this composite this.components
const index = this.components.indexOf(component)
if (index > -1) {
this.components.splice(index, 1)
}
}
detach(): void {
// Detaching this composite from its parent composite
if (this.referenceToParent) {
this.referenceToParent.delete(this)
this.referenceToParent = undefined
}
}
}
|
./src/composite/icomponent.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | import Folder from './folder'
export default interface IComponent {
// A component interface describing the common
// fields and methods of leaves and composites
referenceToParent?: Folder
dir(indent: string): void
// A method each Leaf and composite container should implement
detach(): void
// Called before a leaf is attached to a composite
}
|
Output
node ./dist/composite/client.js
<DIR> root
..<FILE> abc.txt
..<FILE> 123.txt
..<DIR> folder_a
....<FILE> xyz.txt
..<DIR> folder_b
....<FILE> 456.txt
<DIR> root
..<FILE> abc.txt
..<FILE> 123.txt
..<DIR> folder_b
....<FILE> 456.txt
....<DIR> folder_a
......<FILE> xyz.txt
Summary
... To read hidden text, either pause Video Lectures, refer to Book or subscribe to Medium Membership.