C++ Builder Pattern Example

1. Definition

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

2. Problem Statement

Imagine designing a system for constructing a complex meal. A meal could have multiple courses like appetizers, main courses, desserts, and drinks. It's inefficient and confusing to have one giant constructor or setter method for all possible options and combinations of a meal.

3. Solution

The Builder Pattern suggests moving the construction logic out of the actual class and delegating this responsibility to separate objects called builders. Builders construct the final object step by step. This approach allows you to produce different types and representations of an object using the same construction code.

4. Real-World Use Cases

1. Construction of a document converter that can produce different file formats (e.g., PDF, DOCX, RTF) from a single set of data.

2. GUI libraries where you might add components step-by-step to a window.

5. Implementation Steps

1. Declare a builder interface with methods for creating parts of a product.

2. Implement concrete builders corresponding to specific product variations.

3. Define a director class that will use the builder to create a product.

4. The client should instantiate a builder, pass it to the director, and then work with the constructed product.

6. Implementation in C++

// Product
class Meal {
    std::string appetizer;
    std::string mainCourse;
    std::string dessert;
public:
    void setAppetizer(const std::string& app) { appetizer = app; }
    void setMainCourse(const std::string& main) { mainCourse = main; }
    void setDessert(const std::string& des) { dessert = des; }
    void showMeal() {
        std::cout << "Meal consists of: " << appetizer << ", " << mainCourse << ", and " << dessert << "." << std::endl;
    }
};
// Builder interface
class MealBuilder {
public:
    virtual void buildAppetizer() = 0;
    virtual void buildMainCourse() = 0;
    virtual void buildDessert() = 0;
    virtual Meal* getMeal() = 0;
};
// Concrete builder
class ItalianMealBuilder : public MealBuilder {
    Meal* meal;
public:
    ItalianMealBuilder() { meal = new Meal(); }
    void buildAppetizer() { meal->setAppetizer("Bruschetta"); }
    void buildMainCourse() { meal->setMainCourse("Pizza"); }
    void buildDessert() { meal->setDessert("Gelato"); }
    Meal* getMeal() { return meal; }
};
// Director
class Cook {
    MealBuilder* builder;
public:
    Cook(MealBuilder* builder) : builder(builder) {}
    void constructMeal() {
        builder->buildAppetizer();
        builder->buildMainCourse();
        builder->buildDessert();
    }
    Meal* getMeal() {
        return builder->getMeal();
    }
};
int main() {
    MealBuilder* builder = new ItalianMealBuilder();
    Cook cook(builder);
    cook.constructMeal();
    Meal* meal = cook.getMeal();
    meal->showMeal();
    delete meal;
    delete builder;
    return 0;
}

Output:

Meal consists of: Bruschetta, Pizza, and Gelato.

Explanation:

1. Meal represents the product. In this case, it's a meal consisting of an appetizer, main course, and dessert.

2. MealBuilder is the abstract builder interface that specifies methods to build the parts of the product.

3. ItalianMealBuilder is a concrete builder that defines the construction of an Italian meal.

4. Cook is the director who constructs the product using the builder.

5. In the main function, an Italian meal builder is instantiated, passed to the cook (director), and the meal is constructed and displayed.

7. When to use?

Use the Builder pattern when:

1. The algorithm for creating a complex object should be independent of the parts that make up the object and how they are assembled.

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

3. Object construction requires a lot of steps, and these steps should be decoupled from the actual representation.


Comments