×

Search anything:

Builder Pattern implementation in Python

Binary Tree book by OpenGenus

Open-Source Internship opportunity by OpenGenus for programmers. Apply now.

Table of content

  • Introducton to the problem
  • Properties of the pattern
  • Builder pattern UML Diagram
  • Participants
  • Collaborations among the participants
  • Source Code example
  • Time and Space Complexity
  • Conclusion

Introduction to Problem Statement

When creating complex objects that require a step-by-step construction process, the code can become difficult to maintain and bulky. It is especially true when there are multiple representations or variations of the same object, each with its specific construction steps.

The Builder pattern solves this problem by separating the construction process from the object's representation, enabling the creation of different representations using the same construction steps.

Properties of the Builder pattern

The Builder pattern is a creational design pattern that separates the process of constructing a complex object from the parts that make up the object and how they are assembled. It is easily recognizable by its specific structure, which includes a class containing a single creation method, and several other methods that can configure the resulting object. These methods often facilitate chaining, which makes it easier to create complicated objects in a more coherent and maintainable way.

The pattern provides flexibility in constructing different representations of the same object. It follows the Object Composition principle and adheres to the Open/Closed Principle, which suggests that software entities such as classes, modules, and functions should remain closed for modification but open for extension. The process enables the creation of new object types by introducing new Builder classes without modifying the existing ones.

Builder UML Diagram

CarBuilder

Participants

  • Builder (ICarBuilder)
    The ICarBuilder class represents the Builder interface. It defines an abstract interface with methods like set_engine_type, set_tires, set_interior, and set_paint, which specify the methods that the concrete builders must implement to construct the different parts of a car.

  • Concrete Builder (CarBuilder)
    The CarBuilder class is the Concrete Builder that implements the ICarBuilder interface. It provides the actual logic for constructing the various components of a car, such as setting the engine type, tyres, interior, and paint. It creates a specific representation or variation of the final car product.

  • Director
    The Director is responsible for constructing a specific type of car, such as a sports car. It contains a static construct() method that creates an instance of the CarBuilder class and calls its methods in a specific order to build a sports car.

  • Product (Car)
    The Product is the final object created by the Builder classes, containing the state and behaviour of the entity. It is independent of the construction process and assembled by the Builder classes based on the Director's instructions. It promotes code reuse and maintainability, allowing it to be used across multiple construction scenarios without modifications.

Source Code

  1. The Client construct the Director
  2. The Client can use the Director construct() method to handle every stage of the build process.
  3. The Director either delivers the final product to the Client or offes an alternative way for the the Client to obtain it later.

./builder/Interface_car_builder.py

"The Builder Concept (ICarBuilder)"

from abc import ABCMeta, abstractmethod

class ICarBuilder (metaclass = ABCMeta):
    "The Interface"
    
    @staticmethod
    @abstractmethod
    def set_engine_type(engine_type):
        "Set the engine_type"
    
    @staticmethod
    @abstractmethod
    def set_tires(tires):
        "Set the tires"
    
    @staticmethod
    @abstractmethod
    def set_interior(interior):
        "Set the interior"
    
    @staticmethod
    @abstractmethod
    def set_paint(paint):
        "Set the paint"
    
    @staticmethod
    @abstractmethod
    def get_result():
        "Return the final product"

./builder/car_builder.py

"The Concrete Builder Class (CarBuilder)"

from interface_car_builder import ICarBuilder 
from car import Car

class CarBuilder(ICarBuilder):
    "The Car Builder"
    
    def __init__(self):
        #The Product
        self.car = Car()
    
    def set_engine_type(self, engine_type):
        self.car.engine_type = engine_type
        return self
    
    def set_tires(self, tires):
        self.car.tires = tires
        return self
    
    def set_interior (self, interior):
        self.car.interior = interior
        return self
    
    def set_paint (self, paint):
        self.car.paint = paint
        return self
    
    def get_result(self):
        return self.car

./builder/car.py

"The Product (Car)"

class Car():
    
    def __init__(self, engine_type = "V6 engine", tires = "All-terrain tires",
    interior = "Suede interior", paint = "White paint"):
        
        self.engine_type = engine_type
        self.tires = tires
        self.interior = interior
        self.paint = paint
        
    def construction(self): #String method
        "Return a string describing the construction"
        return f"This is a car with a {self.engine_type} and {self.tires}, "\
            f"{self.interior} and external {self.paint}."

./builder/sports_car_director.py

"Sports Car Product"
from car_builder import CarBuilder

class SportsCarDirector:
    """The Director, building a SportsCar """
    @staticmethod    
    def construct():
        return CarBuilder()\
            .set_engine_type("V8 Turbocharged Engine")\
            .set_tires("Performance Tires")\
            .set_interior("Leather Racing Seats")\
            .set_paint("Metallic Gold Paint")\
            .get_result()

./builder/suv_director.py

"SUV Car"
from car_builder import CarBuilder

class SUVDirector:
    """ The Director, building a SUV"""
    @staticmethod
    def construct():
        return CarBuilder()\
            .set_engine_type("V6 EcoBoost Engine")\
            .set_tires("All-Season Tires")\
            .set_interior("Luxury Leather Interior")\
            .set_paint("Deep Blue Sea Paint")\
            .get_result()

./builder/electric_sedan_director.py

"Electric Sedan Car"
from car_builder import CarBuilder

class ElectricSedanDirector:
    """The Director, building an Electric Sedan Car"""
    @staticmethod    
    def construct():
        return CarBuilder()\
            .set_engine_type("Electric Motor")\
            .set_tires("Low Rolling Resistance Tires")\
            .set_interior("Recycled Material Interior")\
            .set_paint("Matte Black Paint")\
            .get_result()

./builder/client.py

""" Car Builder Client """

from sports_car_director import SportsCarDirector
from suv_director import SUVDirector
from electric_sedan_director import ElectricSedanDirector

SPORTS = SportsCarDirector.construct()
SUV = SUVDirector.construct()
ELECTRICSEDAN = ElectricSedanDirector.construct()

print(SPORTS.construction())
print(SUV.construction())
print(ELECTRICSEDAN.construction())

Output

Sports Car
This is a car with a V8 Turbocharged Engine and Performance Tires, Leather Racing Seats and external Metallic Gold Paint.

SUV Car
This is a car with a V6 EcoBoost Engine and All-Season Tires, Luxury Leather Interior and external Deep Blue Sea Paint.

Electric Sedan Car
This is a car with a Electric Motor and Low Rolling Resistance Tires, Recycled Material Interior and external Matte Black Paint.

Time and Space Complexity

Time Complexity

The time complexity of implementing the Builder pattern mainly depends on the operations within the Builder and Director classes, as well as the complexity of the Product class.

In our case, the time complexity of the construction process is determined by the number of method calls required to build the Product (Car) instance. Assuming that each method call (e.g., set_engine_type, set_tires, set_interior, set_paint) takes constant time O(1), the overall time complexity of constructing a Car instance would be O(n), where n is the number of method calls required.

Space Complexity

The space complexity of implementing the Builder pattern is influenced by the memory requirements of the Product class and any additional data structures used within the Builder and Director classes.

In our case, the space complexity is determined by the memory to store the Car instance and its components. As the components are simple data types (Strings), the space complexity would be O(1) or constant space, as the memory needs do not grow particularly with the input size.

Concluion

  • The Builder pattern is a creational design pattern that allows the creation of complex objects through a step-by-step approach.

  • The Builder pattern allows for creating complex objects in any order and gives the Builder the flexibility to include or exclude any available components as required.

  • In the occurrence of a different combination of the product that the Builder does not offer, use a specific Director to construct a customised combination.

Builder Pattern implementation in Python
Share this