Builder

Construct complex objects step by step. Builder lets you produce different types and representations of an object using the same construction process — without telescoping constructors.

The Problem

You need to create a House object. A simple house just needs walls and a roof. But some houses have a garage, a swimming pool, a garden, or a smart home system. The naive solution is a constructor with every possible parameter:

// Telescoping constructor — nightmare to read and call
const house = new House(4, 2, true, false, true, false, 'brick', 'clay', true);
// What does `true, false, true, false` mean? Who knows.

Alternatively, you might create subclasses for every combination: HouseWithGarage, HouseWithPool, HouseWithGarageAndPool… that explodes fast.


The Solution

Separate the construction of a complex object from its representation. A Builder exposes a fluent step-by-step API. An optional Director encodes common construction sequences. You call only the steps you need.

Director - builder: Builder + construct(): void «interface» Builder + reset(): void + buildStep(n): void + getResult(): Product ConcreteBuilder + reset() + buildStep() Product + parts: string[]
Builder — Director drives the Builder interface; ConcreteBuilder produces the Product

Real-World Analogy

A fast food order form is a builder. You specify: burger type, add cheese, extra sauce, no pickles, large fries. The kitchen (builder) assembles your meal step by step. You only specify what you want — you don’t micromanage the assembly process. Different combinations produce different meals without a combinatorial explosion of menu items.


TypeScript Example

// Product
class House {
  walls = 0;
  roof  = '';
  hasGarage = false;
  hasPool   = false;
  hasGarden = false;

  describe(): string {
    const features = [
      `${this.walls} walls`,
      `${this.roof} roof`,
      this.hasGarage ? 'garage' : null,
      this.hasPool   ? 'pool'   : null,
      this.hasGarden ? 'garden' : null,
    ].filter(Boolean).join(', ');
    return `House with: ${features}`;
  }
}

// Builder interface
interface HouseBuilder {
  reset(): this;
  setWalls(n: number): this;
  setRoof(material: string): this;
  addGarage(): this;
  addPool(): this;
  addGarden(): this;
  build(): House;
}

// Concrete builder
class ModernHouseBuilder implements HouseBuilder {
  private house = new House();

  reset(): this {
    this.house = new House();
    return this;
  }

  setWalls(n: number): this {
    this.house.walls = n;
    return this;
  }

  setRoof(material: string): this {
    this.house.roof = material;
    return this;
  }

  addGarage(): this {
    this.house.hasGarage = true;
    return this;
  }

  addPool(): this {
    this.house.hasPool = true;
    return this;
  }

  addGarden(): this {
    this.house.hasGarden = true;
    return this;
  }

  build(): House {
    const result = this.house;
    this.reset(); // ready for next build
    return result;
  }
}

// Director encodes reusable construction sequences
class HouseDirector {
  buildMinimal(builder: HouseBuilder): House {
    return builder.reset().setWalls(4).setRoof('concrete').build();
  }

  buildLuxury(builder: HouseBuilder): House {
    return builder
      .reset()
      .setWalls(6)
      .setRoof('terracotta')
      .addGarage()
      .addPool()
      .addGarden()
      .build();
  }
}

// Usage
const builder  = new ModernHouseBuilder();
const director = new HouseDirector();

const simple = director.buildMinimal(builder);
console.log(simple.describe());
// → House with: 4 walls, concrete roof

const luxury = director.buildLuxury(builder);
console.log(luxury.describe());
// → House with: 6 walls, terracotta roof, garage, pool, garden

// Or use the builder directly without the Director
const custom = builder
  .reset()
  .setWalls(4)
  .setRoof('slate')
  .addGarden()
  .build();
console.log(custom.describe());
// → House with: 4 walls, slate roof, garden

When to Use

  • You need to construct complex objects with many optional parameters or configuration steps
  • You want to produce different representations of a product using the same construction process
  • Your constructors are getting bloated with optional parameters (null, undefined, false noise)
  • You need fine-grained control over the construction process

Pros

  • Eliminates telescoping constructors — only set what you actually need
  • Reuse the same construction code for different product representations
  • Director encapsulates common presets while still allowing custom builds
  • Single Responsibility Principle — building logic is isolated from business logic

Cons

  • Increases code complexity — requires dedicated builder classes
  • Overkill for simple objects; best reserved for objects with many optional parts

  • Abstract Factory creates families of related objects; Builder constructs one complex object step by step
  • Composite trees are often built using a Builder
  • Fluent Interface is a common stylistic choice when implementing builders (method chaining with return this)