Builder Pattern Using JavaScript and ES6

Writing a Builder pattern in JavaScript using ES6, Employing Pseudo-Encapsulation.

Axel Hadfeg
3 min readAug 4, 2017
Like a construction worker crafting a building, the builder pattern ensures that the building code and the product will remain as two separate entities.

Builder patterns are one of the most paramount design patterns found in programming. However, seldom do we find its use in JavaScript; in fact, builder patterns are very hard to come by in JavaScript. JavaScript’s approach to inheritance and exposition is partially to blame because the builder pattern is more suited for languages whose objects abide by data hiding. The perception that JavaScript is not truly object-oriented may render the implementation of these patterns unintuitive. Therefore, the question of why to even bother applying builder patterns in JavaScript prevails. The answer is the same reason that we would apply it in any other language — to separate the complexities of the creation logic from the final representation. The added nuance here is that, as JavaScript developers, we can just as easily access properties from the final rendition as we can rely on the properties within the builder construct. While this notion is true, we can limit accessibility.

The following code block employs ecmascript-2015 (es6). The class keyword looks much nicer and makes our implementation of the builder pattern resemble the schema of languages that are known to employ the pattern more suitably. However, the class is nothing more than syntactic sugar for JavaScript’s prototypal approach to inheritance.

class Raptor {   constructor(build) {
this.specimenId = build.specimenId;
this.speed = build.speed;
this.plumage = build.plumage;
}
static get Builder() {
class Builder {
constructor(specimenId) {
this.specimenId = specimenId;
}
withSpeed(speed) {
this.speed = speed;
return this;
}
withPlumage(plumage) {
this.plumage = plumage;
return this;
}
build() {
return new Raptor(this);
}
}
return Builder;
}
}// We can call build unto our newly constructed builder object ...
let raptorBuilder1 = new Raptor.Builder('244E-C');
let raptor1 = raptorBuilder1.build();
// ... or pass in the builder object as an argument to Raptor.
// Your call.
let raptorBuilder2 = new Raptor.Builder('3998A-D');
let raptor2 = new Raptor(raptorBuilder2);

As an homage to practices I’ve seen in Java and other OO-heavy languages, I decided to include the builder class within the associated parent class. Note that these classes aren’t nested per se (such a thing is not truly possible in JavaScript). This is just a rather nice way of statically acquiring the builder class that will be employed to construct the parent class’s instances. Also note that the fluent pattern has been applied as well so that one can chain multiple properties to a builder object in a single expression. One can also decide to invoke the build method at the culmination of this chain. This is achieved because the fluent methods return a reference to the builder itself.

let raptor3 = new Raptor.Builder('88C')
.withSpeed(45)
.withPlumage('heavy')
.build();

As stated previously, there is very little use for applying the pattern if the final product (the raptor instance, in this example) is just as subject to mutability as the builder object. To accomplish this, we invoke the Object.defineProperties() method, providing the context as the first argument. For the second argument, we define the object’s properties and setting each property’s writability to false. (One can also opt to have the individual enumerabilities tweaked as well. Refer to the Mozilla documentation for more on Object.defineProperties.) I added in a custom-tailored validator to ensure that the builder object that is being passed into the base class’s constructor is the appropriate one. To ensure that the builder meets its purpose, one might want to consider applying a more well-defined validator.

class Raptor {   constructor(build) {
if (arguments.length === 1 && this.validateBuild(build)) {
let specimenId = build.specimenId;
let speed = build.speed;
let plumage = build.plumage;
Object.defineProperties(this, {
'_specimenId': {
value: specimenId,
writable: false
},
'_speed': {
value: speed,
writable: false
},
'_plumage': {
value: plumage,
writable: false
}
});
}
}
validateBuild(build) {
return (String(build.constructor) === String(Raptor.Builder));
}
static get Builder() {
class Builder {
constructor(specimenId) {
this.specimenId = specimenId;
}
withSpeed(speed) {
this.speed = speed;
return this;
}
withPlumage(plumage) {
this.plumage = plumage;
return this;
}
build() {
return new Raptor(this);
}
}
return Builder;
}
}

Inheritance and class extension will have no ill bearing in the structural integrity of the pattern as long as one passes in the builder object as an argument to the extended classes’ parent constructor. On a more advanced level, one can emulate complete data hiding of the builder’s properties by using JavaScript’s WeakMap.

--

--