When we want to create complicated object which has multiple parts, we can use Builder Pattern that separates the building of these parts. This creational process does not care about how these parts are assembled.
In this tutorial, we’re gonna look at 2 ways to implement Builder Pattern in Node.js:
- using Builder function
- using Builder class
Contents
Node.js Builder Pattern example using Builder function
Our Builder function works as a Builder object that can build a Person object.
A Person object has 4 fields: name
, age
, location
, languages
.
PersonBuilder.js
function PersonBuilder() { this.person = {}; this.setName = name => { this.person.name = name; return this; } this.setAge = age => { this.person.age = age; return this; } this.setLocation = location => { this.person.location = location; return this; } this.setLanguages = languages => { this.person.languages = languages; return this; } this.buildInfo = () => this.person; } module.exports = PersonBuilder; |
Now look at the code in app.js:
const PersonBuilder = require('./PersonBuilder'); const jack = new PersonBuilder().setName('Jack').setAge(25).setLanguages(['English', 'German']).buildInfo(); console.log(jack); const adam = new PersonBuilder().setName('Adam').setLocation('US').setLanguages(['English']).buildInfo(); console.log(adam); |
You can see that, instead of using:
const person = new Person('jack', 25, undefined, ['English', 'German']); |
We use PersonBuilder
object with its methods to set value for fields, then we end with buildInfo()
which return a real Person object:
const jack = new PersonBuilder().setName('Jack').setAge(25).setLanguages(['English', 'German']).buildInfo(); |
Run with command: node app.js
. Here is the result:
{ name: 'Jack', age: 25, languages: [ 'English', 'German' ] } { name: 'Adam', location: 'US', languages: [ 'English' ] } |
Node.js Builder Pattern example using Builder class
In this example, we’re gonna use Builder Pattern to create Customer
objects that have fields: name
, age
, location
, languages
.
The difference is that we will split Builder function into Builder class (CustomerBuilder) and Product class (Customer
).
Create Product class for complex object
Customer
represents the complex object:
Customer.js
class Customer { constructor(builder) { this.name = builder.name; this.age = builder.age; this.location = builder.location; this.languages = builder.languages; } showInfo() { console.log(this); } } module.exports = Customer; |
You can see that we pass a builder
object to constructor
method and use available values of builder
object to set value for each parts. So how do we create parts of Customer
object?
We have a builder class:
Create Builder class
CustomerBuilder.js
const Customer = require('./Customer'); class CustomerBuilder { constructor(name) { this.name = name; } setAge(age) { this.age = age; return this; } setLocation(location) { this.location = location; return this; } setLanguages(languages) { this.languages = languages; return this; } buildInfo() { return new Customer(this); } } module.exports = CustomerBuilder; |
Now look at these CustomerBuilder
methods, they are straightforward, we use them to build the value for name
, age
, location
, languages
. The important things we need to notice are:
– return this
at the end of each method, it guarantees that we always have a Builder object after running the method.
– buildInfo()
returns the final product: Customer
object.
Write Client code for testing
app.js
const CustomerBuilder = require('./CustomerBuilder'); const jack = new CustomerBuilder('Jack').setAge(25).setLanguages(['English', 'German']).buildInfo(); jack.showInfo(); const adam = new CustomerBuilder('Adam').setLocation('US').setLanguages(['English']).buildInfo(); adam.showInfo(); |
Run with command: node app.js
. Here is the result:
Customer { name: 'Jack', age: 25, location: undefined, languages: [ 'English', 'German' ] } Customer { name: 'Adam', age: undefined, location: 'US', languages: [ 'English' ] } |
Conclusion
In the tutorial, we’ve known how to implement Nodejs Builder Pattern in 2 ways: using Builder function and using Builder class. Here are some notes:
– We can create complicated objects step by step with encapsulation of construction process that we control abstractly.
– Not comfortable if dealing with objects that can be modified later.
– We duplicate some portion of the code which may have significant impact in some context and turn into an anti-pattern.