Open In App

Builder Pattern | C++ Design Patterns

Last Updated : 30 Oct, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

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++

  • Director: The Director is the main component of the builder pattern it basically responsible for the construction process of the program. It works with a Builder to build an object. The Director knows the actual steps required to build an object, but it does not know the details of how each step is implemented.
  • Builder: The Builder is the main interface or an abstract class that defines the construction steps required to create an object.
  • Concrete Builder: Basically, these are the classes that implement the Builder interface. Each Concrete Builder is responsible for constructing a particular variant of the object.
  • Product: The Product is the complex object that we want to create. The Product class may have methods to access or manipulate these components. It often has multiple parts or components that are built by the Builder.

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:

C++




#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:

  • Product class
  • Builder interface
  • Concrete Builder
  • Director

Diagram Explaining the working of Builders Pattern:

gfg

Explanation of Builder Pattern

  • Computer: The product class that we want to build.
  • ComputerBuilder: An abstract class defining the builder interface.
  • DesktopComputerBuilder: A concrete builder class implementing the ComputerBuilder interface for building desktop computers.
  • ComputerAssembler: The director class responsible for orchestrating the construction process.

Example:

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

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++

  • Complex Object Creation: It simplifies the creation of complex objects by breaking down the construction process into a series of well-defined steps. Each step focuses on a specific aspect of the object’s configuration.
  • Reusability: Builders can be reused to create similar objects with different configurations. This reusability can lead to more efficient and maintainable code.
  • Parameter Validation: Builders can validate the parameters during the construction process, ensuring that only valid configurations are used to create objects.

Disadvantages of the Builder Patterns in C++

  • Code Overhead: Implementing the Builder Pattern may require writing additional code for the builder classes, which can make the codebase more complex, especially for simple objects.
  • Learning Curve: Developers who are not familiar with the Builder Pattern may need some time to understand its concept and how to implement it correctly.
  • Potential for Incomplete Objects: If the client code forgets to call certain builder methods, it’s possible to create incomplete or inconsistent objects, leading to runtime errors.

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.



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads