Builder

Builder

Builder is a creational design pattern that lets you construct complex objects step by step. The pattern allows you to produce different types and representations of an object using the same construction code.

  • Seperates the construction of an object from it's representation
  • Encapsulates object construction
  • Multi step construction process
  • Implementations can vary
  • Client only sees the abstraction

Product:

It's used by the Concrete Builder class. It contains the attributes and functionalities of the class.

Builder:

It's abstract class which contains the methods that will be implemented by the Concrete Builder.

Concrete Builder:

It implements the methods defined in the abstrct Builder class and it uses the objects of class Product.

Director:

It takes the Concrete Builder object and it's method construct() performs the step by step operations and returns the object to assemble the product parts.

Client:

It uses the classes Directorand Builder and their methods to get the required output.

Builder pattern implementation

Let's consider the scenario of building a house. To build the house we need to build basement, walls, windows, roof.

we need the following steps

  1. build basement
  2. build walls
  3. build windows
  4. buils doors
  5. build roof

step1: Let's write the code for the product class House

class House:
    basement = None
    walls = None
    windows = None
    doors = None
    roof = None

    def get_info(self):
        print(f"basement: {self.basement}")
        print(f"walls: {self.walls}")
        print(f"windows: {self.windows}")
        print(f"doors: {self.doors}")
        print(f"roof: {self.roof}\n")

step2: Let's create the abstract builder class AbsHouseBuilder which contains the required methods to build the house.

from abc import ABC, abstractmethod

class AbsHouseBuilder(ABC):
    house = None
    @abstractmethod
    def build_basement(self):
        pass

    @abstractmethod
    def build_walls(self):
        pass

    @abstractmethod
    def build_windows(self):
        pass

    @abstractmethod
    def build_doors(self):
        pass

    @abstractmethod
    def build_roof(self):
        pass

step3: Let's create the concrete builder class SimpleHouseBuilder which implements the abstract methods defined in the class AbsHouseBuilder.

class SimpleHouseBuilder(AbsHouseBuilder):
    def __init__(self):
        self.house = House()

    def build_basement(self):
        self.house.basement = "rock solid basement"

    def build_walls(self):
        self.house.walls = "4 sand brick walls"

    def build_windows(self):
        self.house.windows = "2 wooden windows"

    def build_doors(self):
        self.house.doors = "2 wooden doors"

    def build_roof(self):
        self.house.roof = "Gable roof"

    def get_house(self):
        return self.house

step4: Let's create a director class HouseDirector which will call the builder methods in a specific order to build the product i.e simple house.

class HouseDirector:
    def __init__(self, builder):
        self.builder = builder

    def construct(self):
        self.builder.build_basement()
        self.builder.build_walls()
        self.builder.build_windows()
        self.builder.build_doors()
        self.builder.build_roof()
        return self.builder.get_house()

step5: Let's use the defined classes HouseDirector and SimpleHouseBuilder to create the simple house.

if __name__ == '__main__':
    builder = SimpleHouseBuilder()
    director = HouseDirector(builder)
    house = director.construct()
    house.get_info()

Once, we combine all the above and execute it then it will give us the below output

basement: rock solid basement
walls: 4 sand brick walls
windows: 2 wooden windows
doors: 2 wooden doors
roof: Gable roof

Now, let's say if we want to build a Igloo house then we just need to write IglooHouseBuilder and implement the respective methods and reuse the existing code.

class IglooHouseBuilder(AbsHouseBuilder):
    def __init__(self):
        self.house = House()

    def build_basement(self):
        self.house.basement = "Ice basement"

    def build_walls(self):
        self.house.walls = "round wall"

    def build_windows(self):
        self.house.windows = "no windows"

    def build_doors(self):
        self.house.doors = "1 wooden door"

    def build_roof(self):
        self.house.roof = "Ice roof"

    def get_house(self):
        return self.house

Now, let's update the client code to use the both builders classes

if __name__ == '__main__':
    builders = [SimpleHouseBuilder(), IglooHouseBuilder()]
    for builder in builders:
        director = HouseDirector(builder)
        house = director.construct()
        house.get_info()

It gives the below output

basement: rock solid basement
walls: 4 sand brick walls
windows: 2 wooden windows
doors: 2 wooden doors
roof: Gable roof

basement: Ice basement
walls: round wall
windows: no windows
doors: 1 wooden door
roof: Ice roof

References: