Composite Design Pattern Using Python

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.

Json

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.


Similar Articles