Composite Design Pattern
Video Lecture
Overview
... Refer to Book, pause Video Lectures or subscribe to Medium Membership to read textual content.
Terminology
... Refer to Book, pause Video Lectures or subscribe to Medium Membership to read textual content.
Composite UML Diagram

Source Code
... Refer to Book, pause Video Lectures or subscribe to Medium Membership to read textual content.
./composite/composite_concept.py
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 | "The Composite pattern concept"
from abc import ABCMeta, abstractmethod
class IComponent(metaclass=ABCMeta):
"""
A component interface describing the common
fields and methods of leaves and composites
"""
reference_to_parent = None
@staticmethod
@abstractmethod
def method():
"A method each Leaf and composite container should implement"
@staticmethod
@abstractmethod
def detach():
"Called before a leaf is attached to a composite"
class Leaf(IComponent):
"A Leaf can be added to a composite, but not a leaf"
def method(self):
parent_id = (id(self.reference_to_parent)
if self.reference_to_parent is not None else None)
print(
f"<Leaf>\t\tid:{id(self)}\tParent:\t{parent_id}"
)
def detach(self):
"Detaching this leaf from its parent composite"
if self.reference_to_parent is not None:
self.reference_to_parent.delete(self)
class Composite(IComponent):
"A composite can contain leaves and composites"
def __init__(self):
self.components = []
def method(self):
parent_id = (id(self.reference_to_parent)
if self.reference_to_parent is not None else None)
print(
f"<Composite>\tid:{id(self)}\tParent:\t{parent_id}\t"
f"Components:{len(self.components)}")
for component in self.components:
component.method()
def attach(self, component):
"""
Detach leaf/composite from any current parent reference and
then set the parent reference to this composite (self)
"""
component.detach()
component.reference_to_parent = self
self.components.append(component)
def delete(self, component):
"Removes leaf/composite from this composite self.components"
self.components.remove(component)
def detach(self):
"Detaching this composite from its parent composite"
if self.reference_to_parent is not None:
self.reference_to_parent.delete(self)
self.reference_to_parent = None
# The Client
LEAF_A = Leaf()
LEAF_B = Leaf()
COMPOSITE_1 = Composite()
COMPOSITE_2 = Composite()
print(f"LEAF_A\t\tid:{id(LEAF_A)}")
print(f"LEAF_B\t\tid:{id(LEAF_B)}")
print(f"COMPOSITE_1\tid:{id(COMPOSITE_1)}")
print(f"COMPOSITE_2\tid:{id(COMPOSITE_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)
print()
LEAF_B.method() # not in any composites
COMPOSITE_2.method() # COMPOSITE_2 contains both COMPOSITE_1 and LEAF_A
|
Output
| python ./composite/composite_concept.py
LEAF_A id:2050574298848
LEAF_B id:2050574298656
COMPOSITE_1 id:2050574298272
COMPOSITE_2 id:2050574298128
<Leaf> id:2050574298656 Parent: None
<Composite> id:2050574298128 Parent: None Components:2
<Leaf> id:2050574298848 Parent: 2050574298128
<Composite> id:2050574298272 Parent: 2050574298128 Components:0
|
Composite Example Use Case
... Refer to Book, pause Video Lectures or subscribe to Medium Membership to read textual content.
Composite Example UML Diagram

Source Code
./composite/client.py
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."
from folder import Folder
from file import File
FILESYSTEM = Folder("root")
FILE_1 = File("abc.txt")
FILE_2 = File("123.txt")
FILESYSTEM.attach(FILE_1)
FILESYSTEM.attach(FILE_2)
FOLDER_A = Folder("folder_a")
FILESYSTEM.attach(FOLDER_A)
FILE_3 = File("xyz.txt")
FOLDER_A.attach(FILE_3)
FOLDER_B = Folder("folder_b")
FILE_4 = File("456.txt")
FOLDER_B.attach(FILE_4)
FILESYSTEM.attach(FOLDER_B)
FILESYSTEM.dir()
# now move FOLDER_A and its contents to FOLDER_B
print()
FOLDER_B.attach(FOLDER_A)
FILESYSTEM.dir()
|
./composite/file.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | "A File class"
from interface_component import IComponent
class File(IComponent):
"The File Class. The files are leaves"
def __init__(self, name):
self.name = name
def dir(self, indent):
parent_id = (id(self.reference_to_parent)
if self.reference_to_parent is not None else None)
print(
f"{indent}<FILE> {self.name}\t\t"
f"id:{id(self)}\tParent:\t{parent_id}"
)
def detach(self):
"Detaching this file (leaf) from its parent composite"
if self.reference_to_parent is not None:
self.reference_to_parent.delete(self)
|
./composite/folder.py
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 | "A Folder, that acts as a composite."
from interface_component import IComponent
class Folder(IComponent):
"The Folder class can contain other folders and files"
def __init__(self, name):
self.name = name
self.components = []
def dir(self, indent=""):
print(
f"{indent}<DIR> {self.name}\t\tid:{id(self)}\t"
f"Components: {len(self.components)}")
for component in self.components:
component.dir(indent + "..")
def attach(self, component):
"""
Detach file/folder from any current parent reference
and then set the parent reference to this folder
"""
component.detach()
component.reference_to_parent = self
self.components.append(component)
def delete(self, component):
"""
Removes file/folder from this folder so that self.components"
is cleaned
"""
self.components.remove(component)
def detach(self):
"Detaching this folder from its parent folder"
if self.reference_to_parent is not None:
self.reference_to_parent.delete(self)
self.reference_to_parent = None
|
./composite/interface_component.py
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 component interface describing the common
fields and methods of leaves and composites
"""
from abc import ABCMeta, abstractmethod
class IComponent(metaclass=ABCMeta):
"The Component Interface"
reference_to_parent = None
@staticmethod
@abstractmethod
def dir(indent):
"A method each Leaf and composite container should implement"
@staticmethod
@abstractmethod
def detach():
"""
Called before a leaf is attached to a composite
so that it can clean any parent references
"""
|
Output
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | python ./composite/client.py
<DIR> root id:2028913323984 Components: 4
..<FILE> abc.txt id:2028913323888 Parent: 2028913323984
..<FILE> 123.txt id:2028913323792 Parent: 2028913323984
..<DIR> folder_a id:2028913432848 Components: 1
....<FILE> xyz.txt id:2028913433088 Parent: 2028913432848
..<DIR> folder_b id:2028913433184 Components: 1
....<FILE> 456.txt id:2028913434432 Parent: 2028913433184
<DIR> root id:2028913323984 Components: 3
..<FILE> abc.txt id:2028913323888 Parent: 2028913323984
..<FILE> 123.txt id:2028913323792 Parent: 2028913323984
..<DIR> folder_b id:2028913433184 Components: 2
....<FILE> 456.txt id:2028913434432 Parent: 2028913433184
....<DIR> folder_a id:2028913432848 Components: 1
......<FILE> xyz.txt id:2028913433088 Parent: 2028913432848
|
New Coding Concepts
Conditional Expressions (Ternary Operators).
In ./composite/composite_concept.py, there are two conditional expressions.
Conditional expressions an alternate form of if/else
statement.
| id(self.reference_to_parent) if self.reference_to_parent is not None else None
|
If the self.reference_to_parent
is not None
, it will return the memory address (id) of self.reference_to_parent
, otherwise it returns None
.
This conditional expression follows the format
| value_if_true if condition else value_if_false
|
eg,
| SUN = "bright"
SUN_IS_BRIGHT = True if SUN == "bright" else False
print(SUN_IS_BRIGHT)
|
or
| ICE_IS_COLD = True
ICE_TEMPERATURE = "cold" if ICE_IS_COLD == True else "hot"
print(ICE_TEMPERATURE)
|
or
| CURRENT_VALUE = 99
DANGER = 100
ALERTING = True if CURRENT_VALUE >= DANGER else False
print(ALERTING)
|
Visit https://docs.python.org/3/reference/expressions.html#conditional-expressions for more examples of conditional expressions.
Summary
... Refer to Book, pause Video Lectures or subscribe to Medium Membership to read textual content.