Menu
Coddy logo textTech

Builder Pattern

Part of the Object Oriented Programming section of Coddy's C++ journey — lesson 93 of 104.

The Builder pattern separates the construction of a complex object from its representation, allowing you to create different configurations step by step. This is especially useful when an object has many optional parameters or requires a specific construction sequence.

Instead of a constructor with many parameters (which becomes confusing), the Builder provides clear, named methods for each configuration option:

#include <iostream>
#include <string>

class Pizza {
public:
    std::string dough;
    std::string sauce;
    std::string topping;
    bool cheese;
    
    void describe() const {
        std::cout << dough << " dough, " << sauce << " sauce, "
                  << topping << (cheese ? ", with cheese" : "") << "\n";
    }
};

class PizzaBuilder {
private:
    Pizza pizza;
    
public:
    PizzaBuilder& setDough(const std::string& d) {
        pizza.dough = d;
        return *this;
    }
    
    PizzaBuilder& setSauce(const std::string& s) {
        pizza.sauce = s;
        return *this;
    }
    
    PizzaBuilder& setTopping(const std::string& t) {
        pizza.topping = t;
        return *this;
    }
    
    PizzaBuilder& addCheese() {
        pizza.cheese = true;
        return *this;
    }
    
    Pizza build() { return pizza; }
};

int main() {
    Pizza margherita = PizzaBuilder()
        .setDough("thin")
        .setSauce("tomato")
        .setTopping("basil")
        .addCheese()
        .build();
    
    margherita.describe();
}

Each setter method returns a reference to the builder (return *this), enabling method chaining for a fluent interface. The build() method finalizes and returns the constructed object.

Use Builder when constructing objects with many optional components, when you want readable construction code, or when the same construction process should create different representations.

challenge icon

Challenge

Easy

Let's build a Computer Builder system that lets users configure custom PCs step by step. This is a perfect scenario for the Builder pattern—computers have many optional components, and we want a clean, readable way to assemble different configurations.

You'll organize your code across three files:

  • Computer.h: Define the product class that represents a fully configured computer.

    Your Computer class should store these components as private members: cpu (string), ram (string), storage (string), gpu (string), and hasWifi (boolean). Initialize hasWifi to false and the strings to empty by default.

    Add a public method called showSpecs() that displays the computer's configuration. For each component that has been set (non-empty string), print it on its own line. The wifi capability should only be printed if it's enabled.

  • ComputerBuilder.h: Create the builder class that constructs computers with a fluent interface.

    Your ComputerBuilder class should have a private Computer member that it builds up. Implement these methods, each returning a reference to the builder to enable method chaining:

    • setCPU(const std::string& cpu)
    • setRAM(const std::string& ram)
    • setStorage(const std::string& storage)
    • setGPU(const std::string& gpu)
    • addWifi() — enables the wifi capability

    Also implement a build() method that returns the completed Computer object and resets the builder for potential reuse.

  • main.cpp: Bring everything together to build a custom computer.

    Read four inputs:

    1. CPU model (string)
    2. RAM specification (string)
    3. Storage specification (string)
    4. Whether to include wifi: yes or no

    Use the ComputerBuilder to construct a computer with the provided CPU, RAM, and storage. If the wifi input is yes, also call addWifi(). Note that no GPU is specified in this configuration—the builder should handle optional components gracefully.

    After building, call showSpecs() on the resulting computer to display its configuration.

The showSpecs() method should output in this format (only showing components that were set):

CPU: [value]
RAM: [value]
Storage: [value]
GPU: [value]
Wifi: Enabled

For example, with inputs Intel i7-12700K, 32GB DDR5, 1TB NVMe SSD, and yes:

CPU: Intel i7-12700K
RAM: 32GB DDR5
Storage: 1TB NVMe SSD
Wifi: Enabled

With inputs AMD Ryzen 5 5600X, 16GB DDR4, 512GB SSD, and no:

CPU: AMD Ryzen 5 5600X
RAM: 16GB DDR4
Storage: 512GB SSD

Notice how the Builder pattern makes the construction process readable and flexible—you can easily add or skip components, and the method chaining creates clear, self-documenting code. The GPU line doesn't appear because we never set it, demonstrating how builders handle optional components naturally.

Cheat sheet

The Builder pattern separates the construction of a complex object from its representation, allowing step-by-step configuration. It's useful when an object has many optional parameters or requires a specific construction sequence.

Instead of constructors with many parameters, the Builder provides clear, named methods for each configuration option:

class Pizza {
public:
    std::string dough;
    std::string sauce;
    std::string topping;
    bool cheese;
};

class PizzaBuilder {
private:
    Pizza pizza;
    
public:
    PizzaBuilder& setDough(const std::string& d) {
        pizza.dough = d;
        return *this;  // Enable method chaining
    }
    
    PizzaBuilder& setSauce(const std::string& s) {
        pizza.sauce = s;
        return *this;
    }
    
    PizzaBuilder& setTopping(const std::string& t) {
        pizza.topping = t;
        return *this;
    }
    
    PizzaBuilder& addCheese() {
        pizza.cheese = true;
        return *this;
    }
    
    Pizza build() { return pizza; }
};

// Usage with method chaining
Pizza margherita = PizzaBuilder()
    .setDough("thin")
    .setSauce("tomato")
    .setTopping("basil")
    .addCheese()
    .build();

Each setter method returns return *this to enable method chaining for a fluent interface. The build() method finalizes and returns the constructed object.

Use Builder when constructing objects with many optional components, when you want readable construction code, or when the same construction process should create different representations.

Try it yourself

#include <iostream>
#include <string>
#include "ComputerBuilder.h"

using namespace std;

int main() {
    // Read inputs
    string cpu, ram, storage, wifiChoice;
    getline(cin, cpu);
    getline(cin, ram);
    getline(cin, storage);
    getline(cin, wifiChoice);

    // TODO: Create a ComputerBuilder instance
    
    // TODO: Use method chaining to set CPU, RAM, and storage
    
    // TODO: If wifiChoice is "yes", also call addWifi()
    
    // TODO: Call build() to get the Computer object
    
    // TODO: Call showSpecs() on the built computer to display configuration

    return 0;
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming