Design Patterns Builder

The Builder pattern constructs a complex object step by step. It separates the process of building an object from its final representation, so the same construction process can produce different results depending on the steps you choose.

The Real-World Analogy

Think about ordering a custom burger. You start with a bun, then add a patty, then choose your toppings, add a sauce, and finally wrap it. The kitchen follows a step-by-step process, and the final burger depends on exactly which choices you made at each step. The Builder pattern works identically — you build an object one piece at a time, and the final result depends on which steps you called.

Problem Without Builder

// Constructor with many parameters — confusing and error-prone
House house = new House(4, 2, true, false, "brick", true, null, "flat");
                         ↑  ↑   ↑     ↑      ↑       ↑    ↑     ↑
                       rooms floors garage pool material fence garden roofType
                       
// Which position is which? Easy to mix up. Hard to add optional parts.

Solution With Builder

House house = new HouseBuilder()
    .setRooms(4)
    .setFloors(2)
    .addGarage()
    .setMaterial("brick")
    .build();

// Each step is named. Optional steps are simply skipped.

UML Diagram

«interface»
┌──────────────────────────────┐
│         HouseBuilder         │  ← Builder interface
├──────────────────────────────┤
│ + setRooms(n)                │
│ + setFloors(n)               │
│ + addGarage()                │
│ + addPool()                  │
│ + build(): House             │
└──────────────┬───────────────┘
               │ implements
    ┌──────────┴──────────┐
    │                     │
┌───┴─────────────┐  ┌────┴──────────────┐
│ConcreteBuilder  │  │  LuxuryBuilder    │  ← Concrete Builders
│ (StandardHouse) │  │  (LuxuryHouse)    │
└───────┬─────────┘  └─────────┬─────────┘
        │                      │
        ▼                      ▼
  StandardHouse           LuxuryHouse         ← Products

        ┌──────────────────────┐
        │       Director       │  ← Director (optional)
        ├──────────────────────┤
        │ + makeStarter()      │
        │ + makeFullOption()   │
        └──────────────────────┘

The Director's Role

A Director class is optional but useful. It knows the correct order and combination of builder steps to produce a standard result. The Director hides the step sequence from client code.

Director
  │
  ├── makeStarterHome(builder):
  │     builder.setRooms(2)
  │     builder.setFloors(1)
  │     builder.build()
  │
  └── makeFullOptionHome(builder):
        builder.setRooms(5)
        builder.setFloors(3)
        builder.addGarage()
        builder.addPool()
        builder.build()

Step-by-Step Flow

Client creates a ConcreteBuilder
        │
        ▼
Client creates a Director and passes the builder to it
        │
        ▼
Client calls director.makeFullOptionHome()
        │
        ▼
Director calls builder steps in the right order:
  setRooms(5) → setFloors(3) → addGarage() → addPool()
        │
        ▼
Client calls builder.build()
        │
        ▼
Builder assembles and returns the finished House object

Code Example (pseudocode)

// Product
class House {
    rooms: int
    floors: int
    hasGarage: bool
    hasPool: bool
}

// Builder interface
interface HouseBuilder {
    setRooms(n: int)
    setFloors(n: int)
    addGarage()
    addPool()
    build(): House
}

// Concrete Builder
class ConcreteHouseBuilder implements HouseBuilder {
    private house = new House()

    setRooms(n)   { house.rooms = n;        return this; }
    setFloors(n)  { house.floors = n;       return this; }
    addGarage()   { house.hasGarage = true; return this; }
    addPool()     { house.hasPool = true;   return this; }
    build()       { return house; }
}

// Director
class Director {
    construct(builder: HouseBuilder) {
        builder.setRooms(4).setFloors(2).addGarage();
    }
}

// Client code
builder  = new ConcreteHouseBuilder();
director = new Director();
director.construct(builder);
house = builder.build();

Fluent Builder (Chained Calls)

Many builders return this from each method so you can chain calls in one statement. This style is called a fluent interface.

House myHouse = new ConcreteHouseBuilder()
    .setRooms(3)
    .setFloors(1)
    .addGarage()
    .build();

Builder vs Constructor vs Factory

┌───────────────────┬──────────────────────────────────────────────┐
│ Constructor       │ Pass all values at once — works for simple   │
│                   │ objects, gets confusing with many params     │
├───────────────────┼──────────────────────────────────────────────┤
│ Factory Method    │ Creates one object — hides which subclass    │
│                   │ is created; does not build step by step      │
├───────────────────┼──────────────────────────────────────────────┤
│ Builder           │ Builds complex objects step by step with     │
│                   │ optional parts; clear and readable           │
└───────────────────┴──────────────────────────────────────────────┘

Real-World Uses

  • SQL query builders — Build a SELECT query by chaining .select(), .from(), .where(), .limit() calls.
  • HTTP request builders — Assemble a request with method, URL, headers, and body separately.
  • UI form builders — Construct a form step by step: add fields, add validators, add submit button.
  • Document generators — Build a PDF or HTML report by adding sections, headers, and tables incrementally.

Key Takeaways

  • Builder constructs a complex object step by step instead of one large constructor call.
  • Each builder step sets one part of the object.
  • The Director class (optional) knows the standard sequences of steps.
  • Fluent builders return this from each method to allow readable method chaining.
  • Builder shines when an object has many optional parts or when the construction order matters.

Leave a Comment