In Jurassic Park, creating a new dinosaur is a complex process. Dr. Henry Wu can't just dump all the parameters into one constructor — genetic sequence, diet, habitat, danger level, temperature requirements... dozens of parameters! Fortunately there's the Builder pattern — an elegant way to construct complex objects step by step.
Imagine a dinosaur constructor with many parameters:
1// Constructor with many parameters — unreadable and error-prone
2const dino = new Dinosaur(
3 "Rexy", // name
4 "Tyrannosaurus", // species
5 "carnivore", // diet
6 8, // age
7 12000, // weight
8 "Sector A", // habitat
9 10, // dangerLevel
10 true, // isActive
11 38, // optimalTemp
12 null, // mate
13 ["hunt", "roar"] // abilities
14);
15// Which parameter is which? Easy to mix up the order!Builder lets you construct an object step by step with a readable API based on method chaining:
1class DinosaurBuilder {
2 constructor(name, species) {
3 // Required parameters in constructor
4 this.dinosaur = {
5 name,
6 species,
7 abilities: []
8 };
9 }
10
11 // Each method sets a property and returns this (the builder)
12 setDiet(diet) {
13 this.dinosaur.diet = diet;
14 return this; // Critical! Returns the builder, enabling chaining
15 }
16
17 setAge(age) {
18 this.dinosaur.age = age;
19 return this;
20 }
21
22 setWeight(weight) {
23 this.dinosaur.weight = weight;
24 return this;
25 }
26
27 setHabitat(habitat) {
28 this.dinosaur.habitat = habitat;
29 return this;
30 }
31
32 setDangerLevel(level) {
33 this.dinosaur.dangerLevel = level;
34 return this;
35 }
36
37 setActive(isActive) {
38 this.dinosaur.isActive = isActive;
39 return this;
40 }
41
42 setOptimalTemperature(temp) {
43 this.dinosaur.optimalTemp = temp;
44 return this;
45 }
46
47 addAbility(ability) {
48 this.dinosaur.abilities.push(ability);
49 return this;
50 }
51
52 // build() creates the final object
53 build() {
54 // Validation before building
55 if (!this.dinosaur.diet) {
56 throw new Error("Diet is required!");
57 }
58 return { ...this.dinosaur };
59 }
60}
61
62// Usage — readable and self-documenting
63const rexy = new DinosaurBuilder("Rexy", "Tyrannosaurus")
64 .setDiet("carnivore")
65 .setAge(8)
66 .setWeight(12000)
67 .setHabitat("Sector A")
68 .setDangerLevel(10)
69 .setActive(true)
70 .setOptimalTemperature(38)
71 .addAbility("hunt")
72 .addAbility("roar")
73 .build();
74
75console.log(rexy);
76// { name: "Rexy", species: "Tyrannosaurus", abilities: ["hunt", "roar"],
77// diet: "carnivore", age: 8, weight: 12000, habitat: "Sector A",
78// dangerLevel: 10, isActive: true, optimalTemp: 38 }The key to the Builder pattern is the Fluent API — each method returns
this, allowing calls to be chained:1// Fluent API for building dinosaur queries
2class DinoQueryBuilder {
3 constructor() {
4 this.query = {
5 filters: {},
6 sort: null,
7 limit: 100,
8 offset: 0
9 };
10 }
11
12 whereSpecies(species) {
13 this.query.filters.species = species;
14 return this;
15 }
16
17 whereDiet(diet) {
18 this.query.filters.diet = diet;
19 return this;
20 }
21
22 whereDangerAbove(level) {
23 this.query.filters.minDanger = level;
24 return this;
25 }
26
27 whereActive(isActive = true) {
28 this.query.filters.isActive = isActive;
29 return this;
30 }
31
32 sortBy(field, direction = "asc") {
33 this.query.sort = { field, direction };
34 return this;
35 }
36
37 limitTo(count) {
38 this.query.limit = count;
39 return this;
40 }
41
42 skip(count) {
43 this.query.offset = count;
44 return this;
45 }
46
47 build() {
48 return { ...this.query };
49 }
50
51 execute(database) {
52 const query = this.build();
53 let results = [...database];
54
55 if (query.filters.species) results = results.filter(d => d.species === query.filters.species);
56 if (query.filters.diet) results = results.filter(d => d.diet === query.filters.diet);
57 if (query.filters.minDanger) results = results.filter(d => d.dangerLevel >= query.filters.minDanger);
58 if (query.filters.isActive !== undefined) results = results.filter(d => d.isActive === query.filters.isActive);
59
60 if (query.sort) {
61 results.sort((a, b) => {
62 const dir = query.sort.direction === "asc" ? 1 : -1;
63 return a[query.sort.field] > b[query.sort.field] ? dir : -dir;
64 });
65 }
66
67 results = results.slice(query.offset, query.offset + query.limit);
68 return results;
69 }
70}
71
72// Sample database
73const dinoDatabase = [
74 { name: "Rexy", species: "Tyrannosaurus", diet: "carnivore", dangerLevel: 10, isActive: true },
75 { name: "Blue", species: "Velociraptor", diet: "carnivore", dangerLevel: 8, isActive: true },
76 { name: "Spike", species: "Triceratops", diet: "herbivore", dangerLevel: 4, isActive: true },
77 { name: "Echo", species: "Velociraptor", diet: "carnivore", dangerLevel: 7, isActive: false },
78 { name: "Trike", species: "Triceratops", diet: "herbivore", dangerLevel: 3, isActive: true }
79];
80
81// Building a query with Fluent API
82const dangerousCarnivores = new DinoQueryBuilder()
83 .whereDiet("carnivore")
84 .whereDangerAbove(7)
85 .whereActive()
86 .sortBy("dangerLevel", "desc")
87 .limitTo(5)
88 .execute(dinoDatabase);
89
90console.log("Dangerous carnivores:", dangerousCarnivores);
91// [{ name: "Rexy", ... }, { name: "Blue", ... }]1class EnclosureBuilder {
2 constructor(name) {
3 this.enclosure = {
4 name,
5 type: "standard",
6 fenceVoltage: 10000,
7 size: 1000,
8 features: [],
9 maxCapacity: 5,
10 currentDinosaurs: []
11 };
12 this.errors = [];
13 }
14
15 setType(type) {
16 const validTypes = ["standard", "aquatic", "aviary", "high-security"];
17 if (!validTypes.includes(type)) {
18 this.errors.push(`Invalid enclosure type: ${type}`);
19 }
20 this.enclosure.type = type;
21 return this;
22 }
23
24 setFenceVoltage(voltage) {
25 if (voltage < 5000) {
26 this.errors.push("Fence voltage must be >= 5000V");
27 }
28 this.enclosure.fenceVoltage = voltage;
29 return this;
30 }
31
32 setSize(squareMeters) {
33 if (squareMeters < 100) {
34 this.errors.push("Minimum area is 100 m²");
35 }
36 this.enclosure.size = squareMeters;
37 return this;
38 }
39
40 setMaxCapacity(capacity) {
41 this.enclosure.maxCapacity = capacity;
42 return this;
43 }
44
45 addFeature(feature) {
46 this.enclosure.features.push(feature);
47 return this;
48 }
49
50 addDinosaur(dinosaur) {
51 if (this.enclosure.currentDinosaurs.length >= this.enclosure.maxCapacity) {
52 this.errors.push(`Enclosure full! Max: ${this.enclosure.maxCapacity}`);
53 }
54 this.enclosure.currentDinosaurs.push(dinosaur);
55 return this;
56 }
57
58 build() {
59 if (this.errors.length > 0) {
60 throw new Error(
61 `Enclosure build errors:\n${this.errors.join("\n")}`
62 );
63 }
64 return Object.freeze({ ...this.enclosure }); // Frozen object
65 }
66}
67
68// Building an enclosure
69const raptorPaddock = new EnclosureBuilder("Raptor Paddock")
70 .setType("high-security")
71 .setFenceVoltage(25000)
72 .setSize(5000)
73 .setMaxCapacity(4)
74 .addFeature("electrified-fence")
75 .addFeature("motion-sensors")
76 .addFeature("reinforced-gates")
77 .addDinosaur("Blue")
78 .addDinosaur("Charlie")
79 .addDinosaur("Delta")
80 .build();
81
82console.log(raptorPaddock);Builder doesn't require classes — we can use simple functions:
1function createDinoReport(name) {
2 const report = { name, sections: [] };
3
4 const builder = {
5 addVitals(heartRate, temperature) {
6 report.sections.push({ type: "vitals", heartRate, temperature });
7 return builder;
8 },
9
10 addBehavior(description, threatLevel) {
11 report.sections.push({ type: "behavior", description, threatLevel });
12 return builder;
13 },
14
15 addNote(text) {
16 report.sections.push({
17 type: "note",
18 text,
19 timestamp: new Date().toISOString()
20 });
21 return builder;
22 },
23
24 build() {
25 return {
26 ...report,
27 generatedAt: new Date().toISOString(),
28 totalSections: report.sections.length
29 };
30 }
31 };
32
33 return builder;
34}
35
36// Building a report
37const report = createDinoReport("Rexy")
38 .addVitals(65, 38.2)
39 .addBehavior("Calm, eating regularly", "low")
40 .addNote("New hunting pattern observed")
41 .addVitals(120, 39.1)
42 .addBehavior("Agitated after the storm", "medium")
43 .build();
44
45console.log(JSON.stringify(report, null, 2));Dr. Wu summarizes: "The Builder pattern is like the protocol for creating a dinosaur — step by step, with validation at each stage:"
this, enabling chained callsbuild() checks correctness before creating the objectThe Builder pattern is especially useful when: