Builder Pattern
The Builder pattern separates the construction of a complex object from its representation, allowing the same process to create different representations. Use it when constructing an object requires many steps or configurations that vary independently.
Overview
The Builder pattern decomposes the construction of a complex object into discrete steps, exposed by a builder object. The client calls the steps it cares about in any order, then asks the builder for the final result. It shines when a constructor would otherwise need a dozen parameters, half of them optional.
When to use it
- An object has many optional fields or configuration steps and constructor overloading would explode.
- You want the same construction process to produce different representations (e.g., a query that can render as SQL or as a parameterized object).
- You want construction to be fluent and self-documenting at the call site.
Example
class Pizza {
size: string = "M";
cheese = false;
pepperoni = false;
mushrooms = false;
}
class PizzaBuilder {
private pizza = new Pizza();
size(size: string) { this.pizza.size = size; return this; }
addCheese() { this.pizza.cheese = true; return this; }
addPepperoni() { this.pizza.pepperoni = true; return this; }
addMushrooms() { this.pizza.mushrooms = true; return this; }
build(): Pizza { return this.pizza; }
}
const pizza = new PizzaBuilder()
.size("L")
.addCheese()
.addMushrooms()
.build();Pros
- Constructs complex objects step by step without telescoping constructors.
- Fluent API makes intent obvious at the call site.
- Same builder protocol can produce different concrete results (director + builder).
Cons
- More code than a plain constructor — wasted effort if the object is small.
- Mutable builder state can leak if you reuse the same instance.
- Validation has to live in `build()` rather than in the constructor.