Builder Pattern
The Builder Pattern is a creational design pattern that lets you construct complex objects step by step. It allows you to produce different types and representations of an object using the same construction code.
Problem
Imagine you're creating a class to build houses. A minimal house might just have four walls, a floor, a roof, and a door. But what if some houses also need a garage, a swimming pool, a garden, or various other optional components?
You could use a constructor with many parameters, but this leads to a "telescoping constructor" with many combinations of parameters. Alternatively, you could use setters for optional parameters, but this approach leads to incomplete objects if a required setter is not called.
Components
- Builder: An interface that specifies methods for creating the different parts of a complex object.
- Concrete Builder: Provides implementation for the Builder. It constructs and assembles parts of the product by implementing the Builder interface.
- Director: Constructs objects using the Builder interface. It defines the order in which to call construction steps.
- Product: The complex object being built. Products can vary in structure and representation.
Implementation Example
Here's a basic implementation of the Builder pattern in JavaScript for building a house:
// Product
class House {
constructor() {
this.floors = 0;
this.walls = 0;
this.windows = 0;
this.doors = 0;
this.hasGarage = false;
this.hasSwimmingPool = false;
this.hasGarden = false;
this.hasStatues = false;
}
getDescription() {
let description = `This house has ${this.floors} floor(s), ${this.walls} walls, ${this.windows} windows, and ${this.doors} door(s).`;
if (this.hasGarage) description += " It has a garage.";
if (this.hasSwimmingPool) description += " It has a swimming pool.";
if (this.hasGarden) description += " It has a garden.";
if (this.hasStatues) description += " It has decorative statues.";
return description;
}
}
// Builder Interface
class HouseBuilder {
reset() {
this.house = new House();
return this;
}
buildFloors(count) {
this.house.floors = count;
return this;
}
buildWalls(count) {
this.house.walls = count;
return this;
}
buildWindows(count) {
this.house.windows = count;
return this;
}
buildDoors(count) {
this.house.doors = count;
return this;
}
buildGarage() {
this.house.hasGarage = true;
return this;
}
buildSwimmingPool() {
this.house.hasSwimmingPool = true;
return this;
}
buildGarden() {
this.house.hasGarden = true;
return this;
}
buildStatues() {
this.house.hasStatues = true;
return this;
}
getResult() {
return this.house;
}
}
// Director
class HouseDirector {
constructor(builder) {
this.builder = builder;
}
setBuilder(builder) {
this.builder = builder;
}
constructMinimalHouse() {
return this.builder
.reset()
.buildFloors(1)
.buildWalls(4)
.buildWindows(2)
.buildDoors(1)
.getResult();
}
constructLuxuryHouse() {
return this.builder
.reset()
.buildFloors(3)
.buildWalls(12)
.buildWindows(10)
.buildDoors(3)
.buildGarage()
.buildSwimmingPool()
.buildGarden()
.buildStatues()
.getResult();
}
}
// Client code
const builder = new HouseBuilder();
const director = new HouseDirector(builder);
// Construct a minimal house using the director
const minimalHouse = director.constructMinimalHouse();
console.log(minimalHouse.getDescription());
// Construct a luxury house using the director
const luxuryHouse = director.constructLuxuryHouse();
console.log(luxuryHouse.getDescription());
// Construct a custom house directly with the builder
const customHouse = builder
.reset()
.buildFloors(2)
.buildWalls(8)
.buildWindows(4)
.buildDoors(2)
.buildGarage()
.buildGarden()
.getResult();
console.log(customHouse.getDescription());
When to Use
- When you need to create complex objects with multiple parts and configurations
- When the construction process needs to be independent from the parts and representations that make up the object
- When you want to isolate complex construction code from the object's business logic
- When you want to create different representations of an object using the same construction process
Benefits
- Enables step-by-step construction of complex objects, allowing you to defer construction steps or run steps recursively
- Lets you construct different product representations using the same construction process
- Isolates complex construction code from the business logic of the product
- Provides a clear and readable way to create objects with many configuration options
- Allows you to enforce mandatory steps and maintain consistent state during construction
Real-World Uses
- StringBuilder in Java for efficient string manipulation
- DOM builders in web development
- Meal builders in food ordering applications
- Query builders in SQL libraries
- PDF document generators
- Configuration builders for complex application settings
Interactive Demo: Car Builder
This demo demonstrates how the Builder pattern allows you to create complex objects step by step. Configure a car with various options and see the resulting object.
Basic Details
Exterior Features
Interior Features
Safety Features
Car Specifications
- Select options and click "Build Car" to see the result