Python Builder Design Pattern

1. Definition

The Builder Design Pattern separates the construction of a complex object from its representation, allowing the same construction process to create different representations.

2. Problem Statement

Imagine you're building a meal ordering system. There are numerous meal combinations (burger types, drink types, sides, etc.), and hardcoding every combination would make the code unwieldy, inflexible, and hard to maintain.

3. Solution

Use the Builder pattern to separate the construction process of the meal from the meal itself. Define a step-by-step process for creating a meal and then implement various builders that follow this process to create different kinds of meals.

4. Real-World Use Cases

1. Construction of complex documents, like RTF or HTML files.

2. GUI builders where you create complex layouts step by step.

3. Meal ordering systems with multiple combinations.

5. Implementation Steps

1. Define an abstract Builder with methods for each part of the product.

2. Create concrete builders implementing the Builder interface for various product configurations.

3. Define a Director that constructs the product using the Builder.

4. The client uses the Director and Builder to create the desired product.

6. Implementation in Python

from abc import ABC, abstractmethod
# Product
class Meal:
    def __init__(self):
        self.items = []
    def add_item(self, item):
        self.items.append(item)
    def get_cost(self):
        return sum(item.price for item in self.items)
    def show_items(self):
        return ', '.join(item.name for item in self.items)
# Concrete parts of the product
class Burger:
    name = "Burger"
    price = 5.0
class Drink:
    name = "Drink"
    price = 2.0
# Builder Interface
class MealBuilder(ABC):
    @abstractmethod
    def add_burger(self):
        pass
    @abstractmethod
    def add_drink(self):
        pass
    @abstractmethod
    def get_meal(self):
        pass
# Concrete Builder
class VegMealBuilder(MealBuilder):
    def __init__(self):
        self.meal = Meal()
    def add_burger(self):
        self.meal.add_item(Burger())
    def add_drink(self):
        self.meal.add_item(Drink())
    def get_meal(self):
        return self.meal
# Director
class MealDirector:
    def prepare_veg_meal(self):
        builder = VegMealBuilder()
        builder.add_burger()
        builder.add_drink()
        return builder.get_meal()
# Client Code
director = MealDirector()
meal = director.prepare_veg_meal()
print(f"Meal items: {meal.show_items()}")
print(f"Total Cost: {meal.get_cost()}")

Output:

Meal items: Burger, Drink
Total Cost: 7.0

Explanation:

1. Meal represents the complex object to be built.

2. Concrete parts like Burger and Drink are the parts of the product.

3. The MealBuilder interface defines the steps to build the product.

4. VegMealBuilder provides a specific implementation for the building steps.

5. The MealDirector uses the builder to create a product, separating the construction from the representation.

6. The client interacts with the Director to get the desired product.

7. When to use?

The Builder Pattern is useful when:

1. The algorithm for creating a complex object should be independent of the parts that make up the object.

2. The construction process must allow different representations for the object constructed.

3. The product’s construction has distinct steps that are required for all configurations.


Comments