3. Builder Pattern#
3.1. Introduction#
The Builder Pattern is a creational design pattern that separates the construction of a complex object from its representation, allowing the same construction process to create different representations. It is particularly useful when dealing with objects that have multiple attributes or configurations.
3.2. Motivation#
In software development, there are scenarios where objects need to be created with numerous configuration options or attributes. Directly instantiating such objects with a constructor can lead to a complex and error-prone code. The Builder Pattern addresses this by encapsulating the construction logic and providing a fluent interface to set different attributes.
3.3. Implementation#
Let’s dive into the implementation of the Builder Pattern. In this pattern, we define a builder class responsible for constructing the complex object step by step, and a director class that orchestrates the construction process.
We define a Product
class representing the complex object to be constructed.
class Product:
def __init__(self):
self.part1 = None
self.part2 = None
self.part3 = None
Now, we define a Builder
class with methods to construct different parts of the product and assemble them together.
class Builder:
def __init__(self):
self.product = Product()
def build_part1(self, part1):
self.product.part1 = part1
def build_part2(self, part2):
self.product.part2 = part2
def build_part3(self, part3):
self.product.part3 = part3
def get_product(self):
return self.product
We define a Director
class that takes a builder object and orchestrates the construction process by invoking the builder’s methods.
class Director:
def __init__(self, builder):
self.builder = builder
def construct(self, part1, part2, part3):
self.builder.build_part1(part1)
self.builder.build_part2(part2)
self.builder.build_part3(part3)
3.3.1. Example#
Now, let’s see how we can use the Builder Pattern in a real-life example of building a custom meal at a restaurant.
class MealBuilder:
def __init__(self):
self.builder = Builder()
def prepare_veg_meal(self):
self.builder.build_part1("Salad")
self.builder.build_part2("Vegetable Curry")
self.builder.build_part3("Rice")
return self.builder.get_product()
def prepare_non_veg_meal(self):
self.builder.build_part1("Chicken Soup")
self.builder.build_part2("Grilled Chicken")
self.builder.build_part3("Chicken Tikka")
return self.builder.get_product()
MealBuilder
class acts as the director, while the Builder
class constructs different parts of the meal (Product
). The prepare_veg_meal
and prepare_non_veg_meal
methods demonstrate the flexibility of the Builder Pattern in creating different representations of the product.
meal_builder = MealBuilder()
veg_meal = meal_builder.prepare_veg_meal()
non_veg_meal = meal_builder.prepare_non_veg_meal()
print("Veg Meal:")
print("Part 1:", veg_meal.part1)
print("Part 2:", veg_meal.part2)
print("Part 3:", veg_meal.part3)
print("\nNon-Veg Meal:")
print("Part 1:", non_veg_meal.part1)
print("Part 2:", non_veg_meal.part2)
print("Part 3:", non_veg_meal.part3)
Veg Meal:
Part 1: Chicken Soup
Part 2: Grilled Chicken
Part 3: Chicken Tikka
Non-Veg Meal:
Part 1: Chicken Soup
Part 2: Grilled Chicken
Part 3: Chicken Tikka
3.4. Benefits & Drawbacks#
Benefits
Separates the construction of a complex object from its representation.
Provides a fluent interface for configuring the object’s attributes.
Allows for the construction of different representations using the same construction process.
Drawbacks
Can introduce complexity, especially for objects with a large number of attributes.
Requires the creation of additional classes, which can increase code overhead.