🏗️ Builder Pattern Tutorial

The Builder Pattern is a creational design pattern that lets you construct complex objects step by step, decoupling the construction process from the final representation.

🔧 When to Use It ?

  • Object creation involves many optional parts or configurations.
  • You want to reuse the same construction process to create different representations.

🧩 Scenario

Imagine ordering a custom meal combo at a fast-food restaurant:

You can choose a burger, drink, and side.

A server (Builder) helps you assemble your meal (Product) step by step.

Different meal combos (Representations) are possible from the same process.

🧱 Components of the Builder Pattern

Product: The complex object being built.

Builder: Specifies an abstract interface for building the parts.

ConcreteBuilder: Implements the Builder interface.

Director (optional): Constructs an object using the Builder.

classDiagram
    direction LR

    class Product {
        +addPart(part)
        +__str__()
    }

    class Builder {
        <<interface>>
        +addPartA()
        +addPartB()
        +getResult()
    }

    class ConcreteBuilderA {
        +addPartA()
        +addPartB()
        +getResult()
    }

    class Director {
        -builder: Builder
        +construct()
    }

    Product <.. ConcreteBuilderA : builds
    Builder <|-- ConcreteBuilderA
    Director --> Builder : uses

Lets translate our food order analogy to build different types of burger eg. Vegetarina and Meat burger

Burger.py (Product)

Our Burger is a product which is created using different ingredient eg. Patty (Veg/Meat), toppings, sauce etc.

class Burger():
    def __init__(self):
        self.ingredients = []
    
    def add(self, ingredient):
        self.ingredients.append(ingredient)

    def result(self):
        return "Burge with :" + ",".join(self.ingredients)

BurgerBuilder.py (Builder Interface)

Our Burger Builder provide interface to build Burger Product.

def BurgerBuilder(animal_type: str):

    def __init__(self, burger):
        self.burger = burger
    
    def add_bun(self, bun):
        pass

    def add_patty(self, patty):
        pass
    
    def add_topping(self, topping):
        pass

    def add_sauce(self, sauce):
        pass

    def get_burger(self):
        return self.burger

Implement Concrete Builders

Now we implement concrete burger types:

class VegBurgerBuilder(BurgerBuilder):
    def __init__(self):
        self.burger = Burger()

    def add_bun(self):
        self.burger.add("Whole Wheat Bun")

    def add_patty(self):
        self.burger.add("Veg Patty")

    def add_sauce(self):
        self.burger.add("Tomato Ketchup")

    def get_burger(self):
        return self.burger


class ChickenBurgerBuilder(BurgerBuilder):
    def __init__(self):
        self.burger = Burger()

    def add_bun(self):
        self.burger.add("Sesame Bun")

    def add_patty(self):
        self.burger.add("Chicken Patty")

    def add_sauce(self):
        self.burger.add("Mayonnaise")

    def get_burger(self):
        return self.burger

Implement Director

Finally we implement a director which will cook the burger to a given type

class Cook:

    def __init__(self, builder : BurgerBuilder):
        self.builder = builder

    def make_burger(self):
        self.builder.add_bun()
        self.builder.add_patty()
        self.builder.add_sauce()
        return self.builder.get_burger()

Client Code

burgerBuilder = VegBurgerBuilder()
cook = Cook(burgerBuilder)
burger = cook.make_burger()
print(burger.result())

# Output: Burger with Whole Wheat Bun, Veg Patty, Tomato Ketchup

Benefits of Using Builder Pattern

  • Separates construction logic from the representation.

  • Supports incremental construction and different representations.

  • Promotes immutability and code reusability.

The Builder Pattern here supports multiple key Object-Oriented Programming (OOP) principles. Here’s how:

🔑 1. Single Responsibility Principle (SRP)

“A class should have only one reason to change.”

  • Product class (e.g., Order) holds data and handles representation.

  • Builder class handles construction logic step-by-step.

  • Director class coordinates the construction process.

✅ Separation of concerns makes each class easier to maintain and test.

🔑 2. Encapsulation

  • Construction logic is encapsulated within the Builder.
  • The Client doesn’t need to know how the product is assembled—only how to request it.

🔑 3. Abstraction

  • Defines an abstract Builder interface to build parts of the product.
  • Multiple Concrete Builders can implement this interface differently.

✅ Allows flexibility in constructing different representations of the same object.

🔑 4. Open/Closed Principle (OCP)

“Software entities should be open for extension, but closed for modification.”

✅ Makes the system extensible and robust to change.

🔑 Bonus: Polymorphism

  • Builders can be swapped dynamically thanks to interface-based programming.
  • The Director works with any class that implements the OrderBuilder interface.

✅ Enhances flexibility and decoupling.