Open In App

Builder Design Pattern

The Builder Design Pattern is a creational pattern used in software design to construct a complex object step by step. It allows the construction of a product in a step-by-step fashion, where the construction process can vary based on the type of product being built. The pattern separates the construction of a complex object from its representation, allowing the same construction process to create different representations.



Components of the Builder Design Pattern

1. Product

The Product is the complex object that the Builder pattern is responsible for constructing.



2. Builder

The Builder is an interface or an abstract class that declares the construction steps for building a complex object.

3. ConcreteBuilder

ConcreteBuilder classes implement the Builder interface, providing specific implementations for building each part of the product.

4. Director

The Director is responsible for managing the construction process of the complex object.

5. Client

The Client is the code that initiates the construction of the complex object.

Builder Design Pattern Example

Problem Statement

You are tasked with implementing a system for building custom computers. Each computer can have different configurations based on user preferences. The goal is to provide flexibility in creating computers with varying CPUs, RAM, and storage options.

Implement the Builder design pattern to achieve this, allowing the construction of computers through a step-by-step process. Use the provided components – Product (Computer), Builder interface, ConcreteBuilder (GamingComputerBuilder), Director, and Client

1. Product (Computer)




// Product
class Computer {
  
private:
    string cpu_;
    string ram_;
    string storage_;
   
   
public:
    void setCPU(const std::string& cpu) {
        cpu_ = cpu;
    }
 
    void setRAM(const std::string& ram) {
        ram_ = ram;
    }
 
    void setStorage(const std::string& storage) {
        storage_ = storage;
    }
 
    void displayInfo() const {
        std::cout << "Computer Configuration:"
                  << "\nCPU: " << cpu_
                  << "\nRAM: " << ram_
                  << "\nStorage: " << storage_ << "\n\n";
    }
};

2. Builder




// Builder interface
class Builder {
public:
    virtual void buildCPU() = 0;
    virtual void buildRAM() = 0;
    virtual void buildStorage() = 0;
    virtual Computer getResult() = 0;
};

3. ConcreteBuilder




// ConcreteBuilder
class GamingComputerBuilder : public Builder {
private:
    Computer computer_;
 
public:
    void buildCPU() override {
        computer_.setCPU("Gaming CPU");
    }
 
    void buildRAM() override {
        computer_.setRAM("16GB DDR4");
    }
 
    void buildStorage() override {
        computer_.setStorage("1TB SSD");
    }
 
    Computer getResult() override {
        return computer_;
    }
};

4. Director




// Director
class ComputerDirector {
public:
    void construct(Builder& builder) {
        builder.buildCPU();
        builder.buildRAM();
        builder.buildStorage();
    }
};

5. Client




// Client
int main() {
    GamingComputerBuilder gamingBuilder;
    ComputerDirector director;
 
    director.construct(gamingBuilder);
    Computer gamingComputer = gamingBuilder.getResult();
 
    gamingComputer.displayInfo();
 
    return 0;
}

Complete Combined code for the above example

Below is the full combined code for the above example:




#include <iostream>
#include <string>
 
using namespace std;
 
// Product
class Computer {
public:
    void setCPU(const std::string& cpu) {
        cpu_ = cpu;
    }
 
    void setRAM(const std::string& ram) {
        ram_ = ram;
    }
 
    void setStorage(const std::string& storage) {
        storage_ = storage;
    }
 
    void displayInfo() const {
        std::cout << "Computer Configuration:"
                  << "\nCPU: " << cpu_
                  << "\nRAM: " << ram_
                  << "\nStorage: " << storage_ << "\n\n";
    }
 
private:
    string cpu_;
    string ram_;
    string storage_;
};
 
// Builder interface
class Builder {
public:
    virtual void buildCPU() = 0;
    virtual void buildRAM() = 0;
    virtual void buildStorage() = 0;
    virtual Computer getResult() = 0;
};
 
// ConcreteBuilder
class GamingComputerBuilder : public Builder {
private:
    Computer computer_;
 
public:
    void buildCPU() override {
        computer_.setCPU("Gaming CPU");
    }
 
    void buildRAM() override {
        computer_.setRAM("16GB DDR4");
    }
 
    void buildStorage() override {
        computer_.setStorage("1TB SSD");
    }
 
    Computer getResult() override {
        return computer_;
    }
};
 
// Director
class ComputerDirector {
public:
    void construct(Builder& builder) {
        builder.buildCPU();
        builder.buildRAM();
        builder.buildStorage();
    }
};
 
// Client
int main() {
    GamingComputerBuilder gamingBuilder;
    ComputerDirector director;
 
    director.construct(gamingBuilder);
    Computer gamingComputer = gamingBuilder.getResult();
 
    gamingComputer.displayInfo();
 
    return 0;
}

Output
Computer Configuration:
CPU: Gaming CPU
RAM: 16GB DDR4
Storage: 1TB SSD


This code demonstrates the Builder design pattern where the Computer class is the product, Builder is the interface, GamingComputerBuilder is the concrete builder, ComputerDirector is the director, and the Client assembles the product using the builder and director.

When to use Builder Design Pattern?

The Builder design pattern is used when you need to create complex objects with a large number of optional components or configuration parameters. This pattern is particularly useful when an object needs to be constructed step by step, some of the scenarios where the Builder design pattern is beneficial are:

When not to use Builder Design Pattern?

While the Builder design pattern is beneficial in many scenarios, there are situations where it might be unnecessary. Here are some cases when you might want to reconsider using the Builder pattern:


Article Tags :