Open In App

Builder Pattern | C++ Design Patterns

The builder pattern is defined as a creational design pattern that separates the construction of a complex object from its representation, allowing us to create different representations of an object using the same construction process. It’s beneficial when an object has many optional properties or configurations.

The logic behind the Builder Pattern is to separate the construction of an object from its representations. This separation basically enables us to create different representations of an object using the same construction process.



Key components of the Builder Pattern in C++

Implementation of the Builder Pattern in C++

Example 1: Let’s see the simplified example to understand the concept of the Builder Pattern in more detail:






#include <iostream>
#include <string>
 
// Product class
class Computer {
public:
    void setCPU(const std::string& cpu) {
        cpu_ = cpu;
    }
 
    void setMemory(const std::string& memory) {
        memory_ = memory;
    }
 
    void setStorage(const std::string& storage) {
        storage_ = storage;
    }
 
    void display() {
        std::cout << "CPU: " << cpu_ << std::endl;
        std::cout << "Memory: " << memory_ << std::endl;
        std::cout << "Storage: " << storage_ << std::endl;
    }
 
private:
    std::string cpu_;
    std::string memory_;
    std::string storage_;
};
 
// Builder interface
class ComputerBuilder {
public:
    virtual void buildCPU(const std::string& cpu) = 0;
    virtual void buildMemory(const std::string& memory) = 0;
    virtual void buildStorage(const std::string& storage) = 0;
    virtual Computer getResult() = 0;
};
 
// Concrete Builder
class DesktopComputerBuilder : public ComputerBuilder {
public:
    DesktopComputerBuilder() {
        computer_ = Computer();
    }
 
    void buildCPU(const std::string& cpu) override {
        computer_.setCPU(cpu);
    }
 
    void buildMemory(const std::string& memory) override {
        computer_.setMemory(memory);
    }
 
    void buildStorage(const std::string& storage) override {
        computer_.setStorage(storage);
    }
 
    Computer getResult() override {
        return computer_;
    }
 
private:
    Computer computer_;
};
 
// Director
class ComputerAssembler {
public:
    Computer assembleComputer(ComputerBuilder& builder) {
        builder.buildCPU("Intel i7");
        builder.buildMemory("16GB");
        builder.buildStorage("512GB SSD");
        return builder.getResult();
    }
};
 
int main() {
    DesktopComputerBuilder desktopBuilder;
    ComputerAssembler assembler;
    Computer desktop = assembler.assembleComputer(desktopBuilder);
 
    std::cout << "Desktop Computer Configuration:" << std::endl;
    desktop.display();
 
    return 0;
}

Output
Desktop Computer Configuration:
CPU: Intel i7
Memory: 16GB
Storage: 512GB SSD




So now in this example we have all the four components of Builder Pattern:

Diagram Explaining the working of Builders Pattern:

Explanation of Builder Pattern

Example:

Lets take the example of making a pizza to demonstrate Builder Patter in C++:




#include <iostream>
#include <string>
 
// Product class
class Pizza {
public:
    void setDough(const std::string& dough)
    {
        this->dough = dough;
    }
 
    void setSauce(const std::string& sauce)
    {
        this->sauce = sauce;
    }
 
    void setTopping(const std::string& topping)
    {
        this->topping = topping;
    }
 
    void displayPizza() const
    {
        std::cout << "Pizza with Dough: " << dough
                  << ", Sauce: " << sauce
                  << ", Topping: " << topping << std::endl;
    }
 
private:
    std::string dough;
    std::string sauce;
    std::string topping;
};
 
// Abstract builder class
class PizzaBuilder {
public:
    virtual void buildDough() = 0;
    virtual void buildSauce() = 0;
    virtual void buildTopping() = 0;
    virtual Pizza getPizza() const = 0;
};
 
// Concrete builder for a specific type of pizza
class HawaiianPizzaBuilder : public PizzaBuilder {
public:
    void buildDough() override
    {
        pizza.setDough("Pan Dough");
    }
 
    void buildSauce() override
    {
        pizza.setSauce("Hawaiian Sauce");
    }
 
    void buildTopping() override
    {
        pizza.setTopping("Ham and Pineapple");
    }
 
    Pizza getPizza() const override { return pizza; }
 
private:
    Pizza pizza;
};
 
// Concrete builder for another type of pizza
class SpicyPizzaBuilder : public PizzaBuilder {
public:
    void buildDough() override
    {
        pizza.setDough("Thin Dough");
    }
 
    void buildSauce() override
    {
        pizza.setSauce("Spicy Tomato Sauce");
    }
 
    void buildTopping() override
    {
        pizza.setTopping("Pepperoni and Jalapenos");
    }
 
    Pizza getPizza() const override { return pizza; }
 
private:
    Pizza pizza;
};
 
// Director class that orchestrates the construction
class Cook {
public:
    void makePizza(PizzaBuilder& builder)
    {
        builder.buildDough();
        builder.buildSauce();
        builder.buildTopping();
    }
};
 
int main()
{
    Cook cook;
 
    HawaiianPizzaBuilder hawaiianBuilder;
    cook.makePizza(hawaiianBuilder);
    Pizza hawaiianPizza = hawaiianBuilder.getPizza();
    hawaiianPizza.displayPizza();
 
    SpicyPizzaBuilder spicyBuilder;
    cook.makePizza(spicyBuilder);
    Pizza spicyPizza = spicyBuilder.getPizza();
    spicyPizza.displayPizza();
 
    return 0;
}

Output
Pizza with Dough: Pan Dough, Sauce: Hawaiian Sauce, Topping: Ham and Pineapple
Pizza with Dough: Thin Dough, Sauce: Spicy Tomato Sauce, Topping: Pepperoni and Jalapenos




In this example:

  1. Pizza” is the product class that we want to build.
  2. PizzaBuilder” is an abstract builder class that defines the steps to build a pizza and provides a method to get the final pizza.
  3. HawaiianPizzaBuilder” and “SpicyPizzaBuilder” are concrete builder classes that implement the “PizzaBuilder” interface to construct specific types of pizzas.
  4. “Cook” is the director class that orchestrates the construction process, taking a builder and calling the necessary build methods to create a pizza.
  5. In the “main” function, we create instances of different builders and use the Cook class to build pizzas with specific characteristics.

Advantages of the Builder Patterns in C++

Disadvantages of the Builder Patterns in C++

Conclusion

Builder Patterns are valuable design pattern for creating complex objects with a clear and flexible construction process. It offers advantages such as reusability, and parameter validation. However, it also comes with some overhead in terms of code and may require a learning curve for developers who are new to the pattern.


Article Tags :