Introduction
A composite design pattern is a structural design pattern. It allows a developer to create multiple nested objects of the same type to complete one single system hierarchy.
Players in this pattern,
- Component: this player defines a base contract with a composite class to allow create nested objects.
- Composite: this player, is one who will implement the contract defined by the component.
- Client: this player with the help of a composite player. It will complete the system hierarchy for a particular requirement
We will see with an example.
Requirement
Build a smart city with smart block features.
We will define 3 player
- AbstractBlock: Component
- Block: Composite
- Client
We will build an Abstract block with Add and remove block features. If we want we can implement Add, and Remove features explicitly. In my case, I am using List, by default I will be having Add(append) and Remove features available for adding/removing blocks.
from abc import abstractmethod
class AbstractBlock:
name: str
size: any
sub_blocks = []
@abstractmethod
def build_block(self):
pass
Now implement a Composite object, i.e. Block by implementing/inheriting a component i.e. in our code AbstractBlock.
from abstractBlock import AbstractBlock
class Block(AbstractBlock):
def __init__(self):
self.sub_blocks = []
def build_block(self):
print(f"Block built with Name {self.name} with size {self.size} unit")
In the above code, we can see that we provided the implementation for the BuildBlock method. Based on our need we can make concrete or non-concreate methods as well.
Below is the client class code.
from abstractBlock import AbstractBlock
from block import Block
import json
smartCity = Block()
smartCity.name = "smartCity"
smartCity.size = 10000
smartlayOut = Block()
smartlayOut.name = "SmartlayOut"
smartlayOut.size = 10000
smartCity.sub_blocks.append(smartlayOut)
smartHome = Block()
smartHome.name = "smartHome"
smartHome.size = 1000
smartlayOut.sub_blocks.append(smartHome)
smartRoom = Block()
smartRoom.name = "smartRoom"
smartRoom.size = 1000
smartHome.sub_blocks.append(smartRoom)
smartLocker = Block()
smartLocker.name = "smartLocker"
smartLocker.size = 20
smartRoom.sub_blocks.append(smartLocker)
smartFolder = Block()
smartFolder.name = "smartFolder"
smartFolder.size = 10
smartLocker.sub_blocks.append(smartFolder)
smartFile = Block()
smartFile.name = "smartFile"
smartFile.size = 5
smartFolder.sub_blocks.append(smartFile)
def build(block: AbstractBlock):
block.build_block()
for sub_block in block.sub_blocks:
build(sub_block)
build(smartCity)
print()
print(json.dumps(smartCity, default=lambda o: o.__dict__, sort_keys=True, indent=2))
In the above code, we can see that the client has a unitized composite object and is built first.
- Smart City followed by Smart Layout
- Smart Layout followed by Smart Home
- Smart Home followed by Smart Room
- Smart Room followed by Smart Locker
- Smart Locker followed by Smart Folder
- Smart Folder followed by Smart File
After executing this code, we can see the same hierarchy in JSON format. For easy understating, I serialized the object into JSON. Below is the output snap.
When we have a requirement of implementing some nested objects with the same type, then we can go for this design pattern.
We can extend this composite object to any extent with N numbers of blocks inside blocks.
Summary
Above is a simple example of using a composite design pattern. You can download the uploaded source code and try adding more blocks. I hope it helps.