Builder
Builder is a creational pattern that separates the construction of a complex object into discrete steps, so the same construction procedure can produce objects of different representations. Instead of cramming all parameters into a constructor at once, you set the needed parts one at a time and receive the finished object at the end.
βΆArchitecture Diagram
π ProcessDashed line animations indicate the flow direction of data or requests
You have probably seen a class whose constructor takes more than 10 parameters. You have to memorize which value goes to which field by position, and pass null for every optional parameter. A call like `new House(4, 2, true, false, null, null, true, 'garage', null, 'pool')` is nearly impossible for a reader to parse. The problem compounds when there are multiple representations. Even for the same house, a wood house, stone house, and glass house require different materials and steps. Handling these variations through constructor overloads or conditionals tangles creation logic and representation logic together, so every change carries the risk of affecting other variants.
As software grew more complex, the number of constituent parts in a single object increased rapidly. Database connection pools, HTTP clients, document parsers -- objects with dozens of configuration items became routine. Early approaches coped by adding constructor parameters, exposing setters, or overloading constructors. But these approaches became harder to maintain as parameters grew, and exposing setters let objects leak into an incomplete intermediate state. The Builder pattern emerged from this pressure as a way to 'break creation into steps while keeping the intermediate state hidden from the outside.'
The Builder pattern consists of four roles. The Builder interface declares construction steps such as buildWalls(), buildRoof(), and buildGarage(). A ConcreteBuilder implements those steps, accumulating intermediate state internally. A Director defines the calling order of the build steps, encapsulating a procedure like 'erect walls first, add the roof, then attach the garage.' The key insight is that method-call order equals assembly process. Each step can return this for chaining, or a Director can manage the sequence. Calling getResult() at the end returns the fully assembled object. Passing a different ConcreteBuilder to the same Director produces a different representation through the same procedure.
Builder interface and implementation
interface HouseBuilder {
buildWalls(material: string): this;
buildRoof(type: string): this;
buildGarage(count: number): this;
getResult(): House;
}
class ConcreteHouseBuilder implements HouseBuilder {
private house = new House();
buildWalls(material: string) {
this.house.walls = material;
return this;
}
buildRoof(type: string) {
this.house.roof = type;
return this;
}
buildGarage(count: number) {
this.house.garages = count;
return this;
}
getResult() {
return this.house;
}
}Each method returns this, enabling chaining. Intermediate state stays inside the Builder; the finished object only leaves when getResult() is called.
Director and client
class HouseDirector {
constructLuxuryHouse(builder: HouseBuilder) {
builder
.buildWalls('stone')
.buildRoof('tile')
.buildGarage(2);
}
}
const builder = new ConcreteHouseBuilder();
const director = new HouseDirector();
director.constructLuxuryHouse(builder);
const house = builder.getResult();The Director knows only the build sequence, not the concrete implementation. The client can also call the steps directly without a Director.
Builder and Abstract Factory both deal with complex object creation but focus on different things. Abstract Factory decides 'which product family to create' and generates related objects at once. Builder breaks down 'how to assemble one complex object' into steps. If the only problem is a telescoping constructor, named parameters or an options-object pattern may suffice depending on the language. Builder is truly needed when the construction process itself has an ordering, when the same process must produce different representations, or when intermediate state must not be exposed externally. Builder's downside is the additional classes it introduces. Applying Builder to a simple object just adds complexity. If the constructor has three or four parameters and no variations, direct construction is the better choice.
Commonly Compared Concepts
Abstract Factory
An interface for consistently creating families of related objects
Abstract Factory creates multiple related objects at once as a product family, while Builder assembles a single complex object step by step.
Factory Method
A creational pattern that delegates object creation to subclasses
Factory Method lets subclasses decide which type of object to create, while Builder separates the steps of how to assemble a single object.
The Builder pattern frequently appears when dealing with objects that have many configuration items. SQL query builders are a textbook example. Chaining like `query.select('name').from('users').where('age > 20').orderBy('name')` is the classic Builder application. HTTP client libraries also commonly use a builder for constructing Request objects. URL, headers, body, authentication, and timeout are set step by step, and build() is called at the end. The signal for considering Builder is 'code that forces you to memorize constructor parameter order, repeatedly passes null for optional parameters, or needs to produce multiple variations of the same structure.' Conversely, if the object is simple and has only required fields, a single constructor is sufficient.