Builder
TLDR A "builder" is a pattern which provides a more user-friendly interface for configuring and creating an object.
Problem/context
Classes with a lot of functionality can grow to have many optional parameters and many allowable configurations. These interfaces can become confusing and cumbersome to use. They can also be problematic to use if you don't have all the necessary parameters up-front, forcing the user to progressively keep a reference to parameters until all of them have been gathered.
Concept
We create a class dedicated to tracking configuration choices and that exposes an API which allows users to specify options one at a time. When the user is ready, they can then ask this new class to create a new instance of the object.
Key characteristics
- Specifying configuration and creating a new instance of an object are separate actions.
- Configuration methods return the builder object to enable call-chaining.
- The builder objects are stateful: they store configuration options.
Examples
A builder class that creates a new car object with custom configuration.
// Note: several class/enum definitions have been left out for brevity
const snazzyCar = new CarBuilder()
.withDriveType(new ManualDrive())
.withSeatMaterial(Material.SUEDE)
.withRoofType(new Convertible())
.build()
const wellPricedCar = new CarBuilder().withCheapestOptions().build()
class CarBuilder {
// Builders can also be used to define defaults to reduce the need for users to configure them.
// Different builders can specify different sets of defaults.
private seatMaterial: Material = Material.LEATHER
private driveType: DriveType = new AutomaticDrive()
private roofType: RoofType = new Hardtop()
withSeatMaterial(seatMaterial: Material) {
this.seatMaterial = seatMaterial
return this // returning this object to allow method chaining
}
withDriveType(driveType: DriveType) {
this.driveType = driveType
return this
}
withRoofType(roofType: RoofType) {
this.roofType = roofType
return this
}
// configuration methods can also configure groups of options
withCheapestOptions() {
this.seatMaterial = Material.PLASTIC
this.driveType = new ManualDrive()
this.roofType = new Hardtop()
}
build() {
return new Car({
seatMaterial: this.seatMaterial,
driveType: this.driveType,
roofType: this.roofType,
})
}
}