2026/02/04 / Coding

GoF Design Patterns - Builder

A practical guide to the Builder pattern from the GoF collection:concepts, when to use it, a C++ implementation example, caveats, and sources.

Coding DesignPattern

Hello! I'm Pan-kun.

This post covers the Builder pattern from the GoF (Gang of Four) design patterns.
I explain it practically with sample code in C++, how to design it, when to apply it, and important caveats.

Introduction

The Builder pattern separates the construction of a complex object from its representation, improving readability and extensibility of the creation process.

I'll quote Wikipedia as a primary source:

"The intent of the Builder design pattern is to separate the construction of a complex object from its representation so that the same construction process can create different representations."
(Source: https://en.wikipedia.org/wiki/Builder_pattern)

en.wikipedia.orgBuilder pattern - Wikipedia

Structure (Elements)

A typical composition includes:

  • Product: the complex object to be constructed
  • Builder: defines steps like BuildPartX() and provides GetResult()
  • ConcreteBuilder: implements Builder, holds mutable parts internally, and returns the final Product
  • Director: holds the construction sequence and issues build commands to the Builder. For small cases, the Director is often omitted.

C++ Implementation Example

Below is a minimal C++ example. This is a typical pattern that uses a Director.

C++
// Product.h  -- the object being constructed
#pragma once
#include <string>

struct Product {
    std::string partA;
    std::string partB;
    std::string partC;
};

// Builder.h -- abstract builder
#pragma once
#include <memory>

class Builder {
public:
    virtual ~Builder() = default;
    virtual void BuildPartA() = 0;
    virtual void BuildPartB() = 0;
    virtual void BuildPartC() = 0;
    virtual std::unique_ptr<Product> GetResult() = 0;
};

// ConcreteBuilder.h
#pragma once
#include "Builder.h"
#include "Product.h"
#include <memory>

class ConcreteBuilder : public Builder {
private:
    std::unique_ptr<Product> product_;
public:
    ConcreteBuilder() : product_(std::make_unique<Product>()) {}
    void BuildPartA() override { product_->partA = "Engine"; }
    void BuildPartB() override { product_->partB = "Wheels"; }
    void BuildPartC() override { product_->partC = "Body"; }
    std::unique_ptr<Product> GetResult() override {
        return std::move(product_);
    }
};

// Director.h
#pragma once
#include "Builder.h"

class Director {
public:
    void ConstructSportsCar(Builder& builder) {
        builder.BuildPartA();
        builder.BuildPartB();
        builder.BuildPartC();
    }
    void ConstructToyCar(Builder& builder) {
        builder.BuildPartB();
        // Toy car doesn't need engine
    }
};

// main.cpp
#include <iostream>
int main() {
    ConcreteBuilder builder;
    Director director;
    director.ConstructSportsCar(builder);
    auto product = builder.GetResult();
    std::cout << "Product parts: " << product->partA << ", " << product->partB << ", " << product->partC << "\n";
}

The key point of this implementation is the separation between the construction procedure (the Director) and the concrete construction steps (the ConcreteBuilder). You can achieve variety simply by replacing ConcreteBuilder.

When to Use It

Representative cases where you should consider the Builder pattern:

  • When a constructor has many parameters and most of them are optional (named/optional parameters or factories can be alternatives).
  • When you need staged or complex initialization and want to separate the initialization steps (useful for incremental verification in tests).
  • When you want to produce different representations from the same construction procedure (multiple ConcreteBuilder implementations).
  • When building immutable objects: the Builder assembles a mutable intermediate representation and returns an immutable object at the end for readability.

Alternatives:

  • Language-level named/optional parameters (for concise cases).
  • Factory Method or Abstract Factory (when creation responsibilities are managed by subclasses or family groups).
  • Stepwise helper functions calling constructors (often sufficient for simple cases).

Summary

The Builder pattern is a powerful pattern intended to separate construction procedure and representation.
It is effective when dealing with complex initialization or variable representations.
However, Builder introduces trade-offs around mutability, integration with dependency injection, and test design. Before adopting it, evaluate the impact on creation sites, serialization, and tests.

References and sources:

en.wikipedia.orgBuilder pattern - Wikipedia

That's it for this post.
Next time I'll cover another GoF pattern.
The code shown here is simplified for learning purposes — when adopting patterns in production, make sure to adapt them to your project requirements.

Thanks for reading — Pan-kun!

← GoF Design Pa…← Back to Blog