Builder Design Pattern in Swift

1. Definition

The Builder is a creational design pattern that allows for the step-by-step construction of complex objects using a specific sequence of actions. It lets you construct a product piece by piece, hiding the internal representation.

2. Problem Statement

Constructing a complex object directly within a class might lead to a very convoluted class filled with lots of responsibilities. This would not only make the class hard to understand but also challenging to maintain. For instance, when creating a complicated meal with multiple courses, a direct approach would be cumbersome.

3. Solution

The Builder pattern suggests moving the construction logic out of the main class and putting it into separate builder objects. A builder receives specific instructions regarding which parts of an object to create. It knows how to assemble the object given a particular configuration.

4. Real-World Use Cases

1. Building a complex meal with an appetizer, main course, dessert, and drinks.

2. Forming a personalized computer with custom specifications.

3. Constructing a comprehensive trip itinerary with flights, accommodations, and activities.

5. Implementation Steps

1. Create an interface that specifies the building steps.

2. For each variation of the construction process, implement concrete builders.

3. Designate a director class that constructs the product using the builder's methods.

4. The client must associate the builder with the director and initiate the construction process.

6. Implementation in Swift Programming

// Step 1: Define the product we want to build.
class Meal {
    var courses: [String] = []
    func add(course: String) {
        courses.append(course)
    }
    func displayMeal() {
        for course in courses {
            print(course)
        }
    }
}
// Step 2: Create the builder protocol.
protocol MealBuilder {
    var meal: Meal { get }
    func buildAppetizer()
    func buildMainCourse()
    func buildDessert()
}
// Step 3: Implement concrete builders.
class VegetarianMealBuilder: MealBuilder {
    var meal = Meal()
    func buildAppetizer() {
        meal.add(course: "Salad")
    }
    func buildMainCourse() {
        meal.add(course: "Veggie Burger")
    }
    func buildDessert() {
        meal.add(course: "Fruit Salad")
    }
}
class NonVegetarianMealBuilder: MealBuilder {
    var meal = Meal()
    func buildAppetizer() {
        meal.add(course: "Chicken Wings")
    }
    func buildMainCourse() {
        meal.add(course: "Steak")
    }
    func buildDessert() {
        meal.add(course: "Ice Cream")
    }
}
// Step 4: Define the director.
class MealDirector {
    func prepareMeal(builder: MealBuilder) -> Meal {
        builder.buildAppetizer()
        builder.buildMainCourse()
        builder.buildDessert()
        return builder.meal
    }
}
// Usage
let director = MealDirector()
let vegMeal = director.prepareMeal(builder: VegetarianMealBuilder())
let nonVegMeal = director.prepareMeal(builder: NonVegetarianMealBuilder())
vegMeal.displayMeal()
nonVegMeal.displayMeal()

Output:

Salad
Veggie Burger
Fruit Salad
Chicken Wings
Steak
Ice Cream

Explanation:

1. We first define a Meal class that represents the product we want to build.

2. The MealBuilder protocol specifies methods for building parts of the product.

3. Concrete builders like VegetarianMealBuilder and NonVegetarianMealBuilder implement this protocol, defining how to build each part of the product.

4. The MealDirector class orchestrates the construction process.

5. The client then uses the director and a specific builder 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 and how they're assembled.

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

3. The object's construction requires a lot of steps that could be done in various sequences.


Comments