low-level-design4 min read

Builder Design Pattern Tutorial from Scratch (2026)

Builder Design Pattern Tutorial from Scratch (2026)

Published:  |  Category: Low Level Design  |  Reading time: ~15 min
Builder Design Pattern Tutorial from Scratch (2026)

The Builder pattern separates the construction of a complex object from its representation, allowing the same construction process to create different representations. It is especially valuable when an object requires many optional parameters or a multi-step initialization process that would otherwise lead to telescoping constructors.

This tutorial covers step-by-step construction of complex objects, building immutable objects with Builder, implementing a fluent API (method chaining), and the Director role that orchestrates the construction process. Examples use Java, C++, and Python.

Step-by-Step Construction with Builder

The Builder pattern breaks down object construction into discrete steps, each represented by a method on the builder. A Director class can orchestrate these steps in a specific sequence to produce standardized configurations, while clients can also use the builder directly for custom configurations.

class Pizza {
    private final String dough;
    private final String sauce;
    private final List toppings;

    private Pizza(Builder builder) {
        this.dough = builder.dough;
        this.sauce = builder.sauce;
        this.toppings = List.copyOf(builder.toppings);
    }

    public static class Builder {
        private String dough;
        private String sauce;
        private List toppings = new ArrayList<>();

        public Builder dough(String dough) {
            this.dough = dough;
            return this;
        }

        public Builder sauce(String sauce) {
            this.sauce = sauce;
            return this;
        }

        public Builder addTopping(String topping) {
            this.toppings.add(topping);
            return this;
        }

        public Pizza build() {
            return new Pizza(this);
        }
    }
}

// Usage
Pizza pizza = new Pizza.Builder()
    .dough("whole wheat")
    .sauce("tomato basil")
    .addTopping("mozzarella")
    .addTopping("pepperoni")
    .build();

Fluent API with Method Chaining

Method chaining (fluent interface) is achieved by having each builder method return the builder instance itself. This creates readable, self-documenting code that reads like a domain-specific language. Fluent builders are widely used in libraries like jOOQ, AssertJ, and OkHttp for constructing complex query or configuration objects.

class HttpClientBuilder {
    private String baseUrl;
    private int timeoutMs = 30000;
    private boolean followRedirects = true;
    private Map defaultHeaders = new HashMap<>();

    public HttpClientBuilder baseUrl(String baseUrl) {
        this.baseUrl = baseUrl;
        return this;
    }

    public HttpClientBuilder timeout(int timeoutMs) {
        this.timeoutMs = timeoutMs;
        return this;
    }

    public HttpClientBuilder followRedirects(boolean follow) {
        this.followRedirects = follow;
        return this;
    }

    public HttpClientBuilder defaultHeader(String key, String value) {
        this.defaultHeaders.put(key, value);
        return this;
    }

    public HttpClient build() {
        return new HttpClient(baseUrl, timeoutMs, followRedirects, defaultHeaders);
    }
}

// Usage
HttpClient client = new HttpClientBuilder()
    .baseUrl("https://api.example.com")
    .timeout(10000)
    .followRedirects(false)
    .defaultHeader("Authorization", "Bearer token")
    .build();

Builder in C++ with Move Semantics

C++ builders can leverage move semantics to avoid unnecessary copies when constructing objects with large internal data. The builder collects parameters and then moves them into the final object, making the pattern efficient for resource-intensive objects like database query builders or document generators.

class SQLQuery {
    std::string table;
    std::vector columns;
    std::string whereClause;
    std::string orderBy;

public:
    class Builder {
        std::string table_;
        std::vector columns_;
        std::string whereClause_;
        std::string orderBy_;

    public:
        Builder& from(std::string table) {
            table_ = std::move(table);
            return *this;
        }
        Builder& select(std::vector cols) {
            columns_ = std::move(cols);
            return *this;
        }
        Builder& where(std::string condition) {
            whereClause_ = std::move(condition);
            return *this;
        }
        Builder& orderBy(std::string field) {
            orderBy_ = std::move(field);
            return *this;
        }
        SQLQuery build() {
            return SQLQuery(std::move(table_), std::move(columns_),
                           std::move(whereClause_), std::move(orderBy_));
        }
    };

    std::string toString() const { /* format query */ }
};

// Usage
SQLQuery query = SQLQuery::Builder()
    .select({"id", "name", "email"})
    .from("users")
    .where("age > 18")
    .orderBy("name")
    .build();

Frequently Asked Questions

When should I use the Builder pattern instead of a constructor?

Use Builder when an object requires many parameters (especially optional ones), when construction involves multiple steps that can vary, or when you need to enforce immutability while providing a flexible construction API. Builder eliminates telescoping constructors and improves readability.

What is the difference between Builder and Factory patterns?

Builder focuses on constructing a complex object step by step and returning it as a final step, while Factory creates an object in a single call. Builder gives finer control over the construction process and is ideal for objects with many configuration options.

Is Builder the same as a fluent API?

Builder often uses a fluent API (method chaining), but they are not the same. Fluent API is a design technique for readable method chains. Builder is a creational pattern that may or may not use fluent interfaces.

Do I always need a Director class?

No, the Director is optional. It is useful when you have standard recipes (e.g., a HawaiianPizzaDirector or SpicyPizzaDirector) that encapsulate common construction sequences. For ad-hoc construction, clients can use the builder directly without a Director.

Originally published on Ayodhyyya. Last updated June 1, 2026.