Skip to content

Builder Pattern — Step-by-Step Object Construction

DodaTech Updated 2026-06-24 4 min read

What You'll Learn

You will learn how the Builder pattern tames complex object creation by breaking it into named, readable steps, and how a Director orchestrates those steps for reuse. You will understand the difference between a Builder and a Factory, and when each is appropriate.

Why It Matters

Constructors with ten parameters are error-prone and unreadable. Telescoping constructors scale poorly as optional parameters grow. Builder solves both problems by guiding the caller through a fluent, step-by-step API that makes object creation self-documenting. For example, an HTTP request object with URL, headers, body, query params, timeout, and authentication settings becomes unmanageable as a single constructor. A Builder lets the caller specify only what they need and in any order, while the build() method validates that all required fields are present.

Real-World Use

Query Builders in ORMs construct SQL statements through chained method calls like query.select("name").from("users").where("age > 18"). DodaTech's pipeline editor uses the Builder pattern to construct multi-stage data processing flows where each stage configuration step is validated as you go — the editor won't let you connect an incompatible output to an input, catching errors before the pipeline even runs.

The Pattern

A Director defines the order of construction steps. A Builder interface declares each step. Concrete Builders produce different representations.

from abc import ABC, abstractmethod

class QueryBuilder(ABC):
    @abstractmethod
    def select(self, columns: list) -> "QueryBuilder":
        pass

    @abstractmethod
    def from_(self, table: str) -> "QueryBuilder":
        pass

    @abstractmethod
    def where(self, condition: str) -> "QueryBuilder":
        pass

    @abstractmethod
    def build(self) -> str:
        pass

class PostgresQueryBuilder(QueryBuilder):
    def __init__(self):
        self._query = []

    def select(self, columns: list) -> "PostgresQueryBuilder":
        self._query.append(f"SELECT {', '.join(columns)}")
        return self

    def from_(self, table: str) -> "PostgresQueryBuilder":
        self._query.append(f"FROM {table}")
        return self

    def where(self, condition: str) -> "PostgresQueryBuilder":
        self._query.append(f"WHERE {condition}")
        return self

    def build(self) -> str:
        return " ".join(self._query) + ";"

class SQLDirector:
    def __init__(self, builder: QueryBuilder):
        self._builder = builder

    def simple_select(self, table: str):
        return self._builder.select(["*"]).from_(table).build()

    def filtered_select(self, table: str, condition: str, columns: list):
        return self._builder.select(columns).from_(table).where(condition).build()
builder = PostgresQueryBuilder()
director = SQLDirector(builder)
print(director.simple_select("users"))
print(director.filtered_select("users", "age > 18", ["name", "email"]))
SELECT * FROM users;
SELECT name, email FROM users WHERE age > 18;

Structure

classDiagram
    class Director {
        +construct(builder: Builder)
    }
    class Builder {
        <>
        +buildPartA()
        +buildPartB()
        +getResult(): Product
    }
    class ConcreteBuilder1 {
        +buildPartA()
        +buildPartB()
        +getResult(): Product1
    }
    class ConcreteBuilder2 {
        +buildPartA()
        +buildPartB()
        +getResult(): Product2
    }
    class Product1
    class Product2
    Director --> Builder : uses
    ConcreteBuilder1 ..|> Builder
    ConcreteBuilder2 ..|> Builder
    ConcreteBuilder1 --> Product1 : produces
    ConcreteBuilder2 --> Product2 : produces

Real-World Usage

  • Java StringBuilder — mutable sequence of characters built step-by-step, finalised with toString().
  • Kotlin buildList { } / buildMap { } — idiomatic Builder DSLs for collections.
  • Lombok @<a href="/design-patterns/builder/">Builder</a> — generates Builder classes for Java POJOs at compile time.
  • React useReducer — action dispatching follows a Builder-like pattern for constructing state transitions.
  • Abstract Factory creates families of objects; Builder constructs a single complex object.
  • Strategy can provide different building steps at runtime.
  • Composite often uses Builder to construct tree structures.

Pros and Cons

Pros Cons
Fine-grained control over construction Increases code size with Builder classes
Reusable construction Process Requires a Director for standardisation
Immutable products after build Overkill for objects with few fields
Fluency improves readability Builders with mutable state are not thread-safe

The key insight in the code above is the return self pattern — each Builder method returns the Builder itself, enabling method chaining. The Director encapsulates common construction sequences, so clients don't repeat themselves. Without the Director, each client would manually call .select().from_().build() every time they needed a simple query.

Practice Questions

  1. Refactor a constructor with 7 parameters into a Builder. What trade-offs emerge in terms of code size versus API clarity?
  2. How would you enforce that certain build steps must be called before others? Consider a Builder where from_() must precede where().
  3. Implement a Builder that validates the constructed object before returning it, raising clear error messages for missing required fields.
  4. When does it make sense to omit the Director and let clients use the Builder directly?

Challenge

Create a second concrete Builder (MySQLQueryBuilder) that produces MySQL-compatible SQL syntax. Use the same SQLDirector with both builders and verify the Director works without modification.

Real-World Task

Find a class in your project with more than five constructor parameters or a complex initialisation sequence. Refactor it using the Builder pattern and use DodaTech's complexity analyser to measure the improvement in code maintainability.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro