Builder
The Builder pattern helps... well, build objects! It can be useful in two situations:
- You have an object that could go in a variety of flavors.
- An object uses a non-trivial, multi-step creation process.
When an object could go in a variety of flavors, a common solution is to use a constructor with many parameters that define whether each of these flavors should be used:
1// In JavaScript
2constructor(size, salami, mozzarella, ruccola) { }
An alternative solution is to use the Builder pattern where a separate builder class manages adding flavors to an object and provides methods that make adding flavors explicit.
In the code below, we have two classes: Pizza
represents a pizza but isn't intended to be used to construct pizzas, and PizzaBuilder
is there for the sole purpose of constructing a custom pizza with a set of possible flavors:
1class Pizza {
2 constructor(builder) {
3 this.size = builder.size;
4 this.salami = builder.salami;
5 this.mozzarella = builder.mozzarella;
6 this.ruccola = builder.ruccola;
7 }
8
9 getDescription() {
10 return `This is a ${this.size} cm pizza.`;
11 }
12}
13
14class PizzaBuilder {
15 constructor(size) {
16 this.size = size;
17 this.salami = false;
18 this.mozzarella = false;
19 this.ruccola = false;
20 }
21
22 addSalami() {
23 this.salami = true;
24 return this;
25 }
26
27 addMozzarella() {
28 this.mozzarella = true;
29 return this;
30 }
31
32 addRuccola() {
33 this.ruccola = true;
34 return this;
35 }
36
37 build() {
38 return new Pizza(this);
39 }
40}
At call site, we create a pizza via PizzaBuilder
that makes the customization process very explicit:
1// In JavaScript
2const pizza = new PizzaBuilder(28)
3 .addMozzarella()
4 .addSalami()
5 .addRuccola()
6 .build();
7
8console.log(pizza.getDescription());
Note that many programming languages allow using named parameters on call sites, which can be a valid alternative to using the Builder pattern in some cases:
1var pizza = new Pizza(28, true, false, true);
However, if a builder goes beyond adding parameters to an object and applies custom logic on each step of object construction, then named parameters aren't a viable replacement.