ReviseAlgo Logo

Design Patterns in C++

Builder Pattern

Construct complex objects stepwise using fluent interfaces and method chaining.

Interview: Method chaining using reference returns, managing partial instantiation states, and validating invariants on build.

Last Updated: June 13, 2026 9 min read

The Builder Pattern separates the construction of a complex object from its representation. It allows you to construct objects step-by-step using a fluent interface.

Fluent Interface

Returns references to the builder (Builder&) from construction methods to enable method chaining.

Stepwise Build

Allows partial initialization. You specify parameters in separate steps, improving code readability.

Invariant Validation

Perform validation checks inside the final build() method to ensure only valid objects are instantiated.

Method Chaining Mechanism

To support method chaining, builder methods must return a reference to the builder itself: Builder& setCpu(std::string cpu) { m_cpu = cpu; return *this; }

Returning *this as a reference prevents copy overhead and allows calling multiple configuration methods in a single statement.

Code Walkthrough

A fluent computer assembly builder implementing invariant checks before creation.

#include <iostream>
#include <string>

class Computer { public: std::string m_cpu; std::string m_gpu; int m_ramGB = 0;

void printSpec() { std::cout << "PC Spec: [CPU: " << m_cpu << " | GPU: " << m_gpu << " | RAM: " << m_ramGB << "GB]\n"; } };

class ComputerBuilder { private: Computer m_computer;

public: // Fluent setters returning a reference to the builder ComputerBuilder& setCPU(const std::string& cpu) { m_computer.m_cpu = cpu; return *this; }

ComputerBuilder& setGPU(const std::string& gpu) { m_computer.m_gpu = gpu; return *this; }

ComputerBuilder& setRAM(int ramGB) { m_computer.m_ramGB = ramGB; return *this; }

// Final build execution containing validation Computer build() { if (m_computer.m_cpu.empty()) { throw std::runtime_error("Computer requires a CPU specification."); } if (m_computer.m_ramGB < 4) { throw std::runtime_error("RAM allocation must be at least 4GB."); } return m_computer; // Return configured object } };

int main() { try { // Method chaining configuration Computer myPC = ComputerBuilder() .setCPU("Intel i9") .setGPU("RTX 4090") .setRAM(32) .build();

myPC.printSpec(); } catch (const std::exception& e) { std::cerr << "Build error: " << e.what() << std::endl; } return 0; }

Interview-Relevant Information

Q: How does returning references from builder methods enable method chaining?
Answer: Returning a reference (Builder&) to the builder object (*this) allows calling another method on the returned reference in the same statement, enabling method chaining.

Q: Where should you validate object invariants in the Builder pattern?
Answer: Validation should be performed inside the final build() method. This ensures that the constructor of the target object is only called if all configurations are valid, preventing the creation of partially initialized or invalid objects.

Quick Checklist

Did you return references to the builder from setter methods? Did you implement validation inside build()? If yes, your Builder design is solid.

Use Cases

Constructing HTTP requests or SQL queries step-by-step with optional query parameters.

Configuring complex objects (like game engine render pipelines or UI frames).

Common Mistakes

Returning copies of the builder instead of references, introducing copy overhead and breaking method chaining.

Instantiating the target object directly without checking if the configuration is valid.