← All Patterns
creational4 participants

Builder

Separate the construction of a complex object from its representation so that the same construction process can create different representations.

The Problem

A constructor with 8+ parameters is hard to read and error-prone. Optional parameters cause telescoping constructors or messy overloads. The order of arguments is non-obvious and parameters of the same type are silently swappable.

Structure

Client.threads(8).events(1M)SimConfigBuilder.threads(n) → *this.events(n) → *this.sync(mode) → *this.verbose() → *this.build() → ConfigSimConfigthreads = 8events = 1Msync = fineMethod chaining: each setter returns *this → fluent interface

Execution Walkthrough

1

Define product

Product (SimConfig) has many optional fields with sensible defaults. Direct construction is unreadable.

2

Builder methods

3

Chain calls

4

build()

5

Reuse builder

Code Comparison

Telescoping constructors — which int is which? Swapping threads and events compiles fine but produces wrong results.

// BAD: which argument is threads? events? seed?
SimConfig cfg1(8, 1000000, true, "fine_grained", 42);
SimConfig cfg2(1000000, 8, false, "coarse", 0);
//             ^^^^^^^ oops — threads/events swapped
//             compiles fine, runs wrong

// Overload explosion:
SimConfig(int threads);
SimConfig(int threads, int events);
SimConfig(int threads, int events, bool verbose);
SimConfig(int threads, int events, bool verbose, std::string sync);
// ... 16 overloads for 4 optional params

Full C++ Implementation

C++ · Builder
#include <string>
#include <stdexcept>
#include <iostream>

struct SimConfig {
  int         threads   = 1;
  int         events    = 10'000;
  bool        verbose   = false;
  std::string sync_mode = "coarse";
  uint32_t    seed      = 0;
};

class SimConfigBuilder {
  SimConfig cfg_;
public:
  SimConfigBuilder& threads(int n)         { cfg_.threads   = n; return *this; }
  SimConfigBuilder& events(int n)          { cfg_.events    = n; return *this; }
  SimConfigBuilder& verbose()              { cfg_.verbose   = true; return *this; }
  SimConfigBuilder& sync(std::string mode) { cfg_.sync_mode = std::move(mode); return *this; }
  SimConfigBuilder& seed(uint32_t s)       { cfg_.seed      = s; return *this; }

  SimConfig build() {
    if (cfg_.threads < 1)
      throw std::invalid_argument("threads must be >= 1");
    return cfg_;
  }
};

int main() {
  auto cfg = SimConfigBuilder{}
    .threads(8)
    .events(1'000'000)
    .sync("fine_grained")
    .verbose()
    .build();

  std::cout << "threads: "   << cfg.threads   << "\n";
  std::cout << "events: "    << cfg.events    << "\n";
  std::cout << "sync: "      << cfg.sync_mode << "\n";
  std::cout << "verbose: "   << cfg.verbose   << "\n";
}

Participants

BuilderConcreteBuilderDirectorProduct

Where it is used

SQL query builders

QueryBuilder.select("*").from("events").where("t > 0").limit(100).build()

Protobuf / gRPC

MessageLite::Builder assembles fields before build() produces an immutable, validated message.

HTTP clients

libcurl: set URL, headers, timeout, auth step-by-step before curl_easy_perform().

Simulation config

SimConfigBuilder sets thread count, event limit, sync mode, and verbosity before engine construction.

Factory MethodSingleton