Design Patterns: Builder Pattern

These notes were adapted from various readings detailed in the References section.


A class can provide a public static factory method, which when called, returns an instance of a class. The immediate advantage in providing an invokable static factory method as a means for object construction is that unlike constructors, the static factory method actually has a name.

Parameters to a constructor generally don’t and in order to get different types of instantiation methods for an object, we typically must provide a number of constructors varying only in parameter list size.

// Pseudo Code!!

class Test {
    constructor(firstParam, secondParam) {}
    constructor(firstParam, secondParam, thirdParam) {}
    constructor(firstParam, secondParam, thirdParam, fourthParam) {}
    constructor(firstParam, secondParam, thirdParam, fourthParam, fifthParam) {}
}

It is difficult to remember how constructors take in parameters. It can be easier to get object instances from a static factory method. A static factory method can also be leveraged to optionally return a new instance of the class, or reuse an existing instance.

Just be careful in that within the perspective of learning new API, having static factory methods may cause a programmer reading code, or documentation to certainly miss the static factory method altogether, and cause difficulty in discerning the different approaches in constructing objects for your class. To minimize the chances of this occurring, we can follow some best practices in naming static factory methods with from, of, getInstance, create, newInstances, as some common method names for returning new instances of objects. It is harder to overlook these types of methods.

As it turns out, static factory methods are not bullet proof either. One can start to overload static factory methods and thus running into the same type of issue with constructors only varying in parameter list size.

If you have too many parameters in your static factory methods, and your constructor, then consider using the Builder Pattern.

Have a separate class which is the Builder that will receive the necessary parameters for the object to be constructed. In this Builder class, make a static method build available to be called to retrieve a new instance with the set of parameters from previous builder calls.

Such an implementation may look like this:

class NutritionFacts {
    private servingSize;
    private servings;
    private calories;
    private fat;
    private sodium;
    private carbohydrates;
    
    private constructor(builder) {
        this.servingSize = builder.servingSize;
        this.servings = builder.servings;
        this.calories = builder.calories;
        this.fat = builder.fat;
        this.sodium = builder.sodium;
        this.carbohydrate = builder.carbohydrate;
    }
    
    public static Builder(servingSize, servings) {
        let _servingSize = servingSize;
        let _servings = servings;
        
        let calories = 0;
        let fat = 0;
        let sodium = 0;
        let carbohydrate = 0;
        
        return {
            calories: (val) => {
                calories = val;
                return this;
            },
            fat: (val) => {
                fat = val;
                return this;
            },
            carbohydrate: (val) => {
                carbohydrate = val;
                return this;
            },
            build: () => {
                return new NutritionFacts(this);
            }
        };
    }
}

Notice the following:

  • The constructor to the NutritionFacts class is private, and thus cannot be directly instantiated.
  • NutritionFacts does expose a static Builder method which can be called to return back an instance of Builder which can be used to return a new NutritionFacts method.
  • The client must set the appropriate parameters by mutating the Builder object.
  • When a new instance of NutritionFacts is needed, then build from the Builder object will then return the instance.

References

  • Effective Java, 3rd ed. - Joshua Bloch - https://www.goodreads.com/book/show/34927404-effective-java