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.
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,falsenoise) - 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
Related Patterns
- 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)