Different Ways to Implement Python Builder Pattern

Zeeshan Afridi Oct 10, 2023
  1. The Builder Pattern, Its Importance and Working
  2. Different Ways to Implement Builder Pattern in Python
  3. Advantages & Disadvantages of Builder Pattern in Python
Different Ways to Implement Python Builder Pattern

The Builder pattern is a pattern that allows the creation of an object to be separated from its representation. This pattern can create and configure complex objects without inheritance, which is a powerful but inflexible approach.

The builder pattern can also be utilized to provide different implementations of a class at run-time or to allow users to construct new objects without having access to the source code.

This tutorial educates about builder patterns and how they work. It also demonstrates different ways to implement Python builder patterns.

The Builder Pattern, Its Importance and Working

The builder pattern is the software design pattern that allows the construction of complex objects to be created step-by-step, providing a new level of detail or functionality.

This pattern is often used in software development, where different software components must be combined to form a complete system.

The builder pattern can be utilized to create anything from simple objects, like a table or a chair, to more complex systems, like a computer or an aeroplane. It is also helpful for creating objects that need to be created in a specific order.

The Python standard library provides a module called builder, making it easy to use the Builder pattern. The builder module provides two classes, the Builder and the Director, which work together to create objects.

The Builder class is responsible for creating the object parts, and the Director class is used for assembling the components into the final object.

To use the Builder pattern, you first create a Builder object, which you use to create the parts of the object. Then you create a Director object, which you use to assemble the pieces into the final object.

Finally, you can call the build() method on the Director object, which returns the final object.

Different Ways to Implement Builder Pattern in Python

There are two methods to implement the Builder Pattern in Python:

  1. The __init__ method
  2. The __new__ method

Use the __init__ Method

The __init__ method is the most common way to implement the Builder Pattern in Python. It is called when the object is created, allowing you to set the values of its attributes.

Use the __new__ Method

The __new__ method is less common but more powerful. The __new__ method is called when the class is created, allowing you to create the object itself.

It means that you can control how the object is created and even create objects that are not the same type as the class.

Advantages & Disadvantages of Builder Pattern in Python

There are some advantages of builder patterns that are listed below:

  • The essential advantage of the builder pattern is that it allows for creating complex objects step-by-step, providing a new level of detail or functionality.
  • This makes creating complex objects much simpler and more efficient.
  • The builder pattern is also advantageous because it allows for a high degree of flexibility, as different builders can create different types of objects.

There are a few disadvantages to using the builder pattern as well.

  • One is that creating an object can be quite time-consuming, as each step needs to be carried out in order.
  • Another disadvantage is that the builder pattern can be quite complex and require a lot of code to be written.

Code Example:

from __future__ import annotations
from abc import ABC, abstractmethod
from typing import Any


class Builder(ABC):
    @property
    @abstractmethod
    def product(self) -> None:
        pass

    @abstractmethod
    def produce_a(self) -> None:
        pass

    """
    This class will provide specific working of the building steps by using the builder interface.
    """


class ConcreteBuilder1(Builder):
    def __init__(self) -> None:
        """This fresh builder instance will contain a blank product object, which is
        used in further assembly.
        """
        self.reset()

    def reset(self) -> None:
        self._product = Product1()

    @property
    def product(self) -> Product1:

        product = self._product
        self.reset()
        return product

    def produce_a(self) -> None:
        self._product.add("PartA")


class Product1:
    """
    When your products are complex and demand extensive configuration, it's possible to use the Builder pattern only.
    """

    def __init__(self) -> None:
        self.parts = []

    def add(self, part: Any) -> None:
        self.parts.append(part)

    def list_parts(self) -> None:
        print(f"The Product parts: {', '.join(self.parts)}", end="")


class Director:
    """
    The client can control builders directly, so the Director class is
     optional. In this case, the Director is only responsible for executing the building steps in a specific sequence.
    """

    def __init__(self) -> None:
        self._builder = None

    @property
    def builder(self) -> Builder:
        return self._builder
        """
    The Director can construct several product variations using the same
    building steps.
    """

    @builder.setter
    def builder(self, builder: Builder) -> None:
        self._builder = builder

    def build_minimal_viable_product(self) -> None:
        self.builder.produce_a()

    def build_full_featured_product(self) -> None:
        self.builder.produce_a()


if __name__ == "__main__":

    director = Director()
    builder = ConcreteBuilder1()
    director.builder = builder

    print("Our Standard primary product:")
    director.build_minimal_viable_product()
    builder.product.list_parts()

    print("\n")

    print("Our Standard full-featured product:")
    director.build_full_featured_product()
    builder.product.list_parts()

    print("\n")
    # We can also use the Builder pattern without a Director class.
    print("Our Custom product: ")
    builder.produce_a()
    builder.product.list_parts()

Output:

Our Standard primary product:
The Product parts: PartA

Our Standard full-featured product:
The Product parts: PartA

Our Custom product: 
The Product parts: PartA

Remember that the Builder pattern is also a design pattern that helps to build complex objects by separating construction and representation. This separation allows different representations to be made from the same construction process.

Zeeshan Afridi avatar Zeeshan Afridi avatar

Zeeshan is a detail oriented software engineer that helps companies and individuals make their lives and easier with software solutions.

LinkedIn