Design Patterns - The Builder Pattern in JavaScript

TL; DR

Source code can be found on GitHub, jsBuilder repo


Design Patterns - The Builder

The Builder pattern is a creational design pattern, which should be applied when complex objects have to be created. When applying this pattern the goal is to separate the object creation (or initialization) logic from the representation.

This pattern should be used when object creation is complicated and involves a lot of code.

UML diagram1 with the Builder pattern:

Builder Pattern with UML diagram

This pattern has three components, the Director which uses the Builder to create objects, the role of the Director is to coordinate the object creation in case there are multiple Builders involved. The Director also provides an extra abstraction layer when implementing this pattern.
The Builder is an abstraction layer above all the Concrete Builders which create the objects. In JavaScript the Builder is not separated clearly and in some case it can be left out completely (although I forced it to be in the sample code).
The Concrete Builders implement the real object creation. Concrete Builders, internally, can use Factories for building parts of the final object (I used factories in my implementation to demonstrate this).

I present the Builder pattern using an analogy.

Lets imagine the process of "building" a cake. In this analogy a cake is considered a complex object, because it is hard to create. The cake object has three parts, the layers, the cream and the topping.

I modeled this scenario in code. Let's start from the Director implementation. In this scenario the Director or Coordinator is represented by the PastryCook function, this handles everything related to "building" the cake.

function PastryCook() {  
    var chocolateCakeBuilder = ChocolateCakeBuilder.getBuilder();
    var strawberryCakeBuilder = StrawberryCakeBuilder.getBuilder();
    var traditionalCakeBuilder = TraditionalCakeBuilder.getBuilder();

    return {
        buildCake: function(flavor) {
            var cake = null;
            switch (flavor) {
                case 'chocolate':
                    cake = chocolateCakeBuilder.buildCake();
                    break;
                case 'strawberry':
                    cake = strawberryCakeBuilder.buildCake();
                    break;
                default:
                    cake = traditionalCakeBuilder.buildCake();
                    break;
            }

            return cake;
        }
    };
}

The PastryCook has a buildCake function, which receives the flavor argument. Based on the value of the argument I used different Builders to build the cake. By adding the possibility to create different cake objects based on a parameter passed to the Director I could demonstrate how to use multiple builders.

The implementation of ChocolateCakeBuilder handles the steps of building a chocolate cake. In this Builder I used factories to simplify the implementation and to provide an extra layer of abstraction.

function ChocolateCakeBuilder() {  
    var layerFactory = LayerFactory.getInstance();
    var creamFactory = CreamFactory.getInstance();
    var toppingFactory = ToppingFactory.getInstance();

    return {
        buildCake: function() {
            return {
                layer: layerFactory.getStandard(),
                cream: creamFactory.getChocolate(),
                topping: toppingFactory.getChocolate()
            }
        }
    };
}

Every Builder implements the buildCake function, this is the method which delivers the Product (referring to the UML diagram).

The StrawberryCakeBuilder is very similar in implementation:

function StrawberryCakeBuilder() {  
    var layerFactory = LayerFactory.getInstance();
    var creamFactory = CreamFactory.getInstance();
    var toppingFactory = ToppingFactory.getInstance();

    return {
        buildCake: function() {
            return {
                layer: layerFactory.getLowCarb(),
                cream: creamFactory.getWhipped(),
                topping: toppingFactory.getStrawberry()
            }
        }
    };
}

For the Builders I used Factories, because these (in general) add more flexibility to the code and reduce the maintenance cost of the codebase.

The source code of ToppingFactory is below; all it does is return a string with the selected topping.

function ToppingFactory() {  
    return {
        getChocolate: function() {
            return 'Topping: [Chocolate]';
        },

        getVanilla: function() {
            return 'Topping: [Vanilla]';
        },

        getStrawberry: function() {
            return 'Topping: [Strawberry]';
        },
    };
}

module.exports = {  
    getInstance: ToppingFactory
};

Sample code was written for node, the index.js has the demo code:

var PastryCook = require('./PastryCook');

var cakeBuilder = PastryCook.getBuilder();

var chocolateCake = cakeBuilder.buildCake('chocolate');  
var strawberryCake = cakeBuilder.buildCake('strawberry');  
var cake = cakeBuilder.buildCake();

console.log("The Chocolate cake is compound of:");  
console.log(JSON.stringify(chocolateCake, null, 2));

console.log("The Strawberry cake is compound of:");  
console.log(JSON.stringify(strawberryCake, null, 2));

console.log("The cake is compound of:");  
console.log(JSON.stringify(cake, null, 2));

Once the code is executed the output should be:

The Chocolate cake is compound of:  
{
  "layer": "Layer: [Standard]",
  "cream": "Cream: [Chocolate]",
  "topping": "Topping: [Chocolate]"
}
The Strawberry cake is compound of:  
{
  "layer": "Layer: [LowCarb]",
  "cream": "Cream: [Whipped]",
  "topping": "Topping: [Strawberry]"
}
The cake is compound of:  
{
  "layer": "Layer: [Standard]",
  "cream": "Cream: [Peanut Butter]",
  "topping": "Topping: [Strawberry]"
}

I tried to keep the implementation clean and developer friendly so the real use case of the Builder design pattern can be seen and understood.

If you enjoyed reading this blog post, please share it or like the Facebook page of the website to get regular updates.

Thank you for reading!

  1. Image Source is Wikipedia