DEV Community

Sheng Wu
Sheng Wu

Posted on

Effective Java - Why do we need a builder?

Why do we need a builder?

Using a constructor or a static factory method has a limit - they don't scale well. If there are many fields in the class, the argument list of a constructor or a static factory method is quite long and we might get the order wrong when calling the method. It is even worse if many fields are optional. A builder class comes in handy.

What is a builder

A builder class is normally a static member class of the class it builds. Instead of making the desired object directly, a client calls the constructor with the required fields and get a builder object. Then the client calls the setter-like methods to set fields. Finally the client calls a parameterless build method to generate the object of interest.

Consider this scenario, we want to cook a recipe and many ingredients are optional.

public class ChickenCongeeRecipe {
    private final int riceWeight;
    private final int waterWeight;
    private final int chickenWeight;
    private final int saltWeight;
    private final int gingerWeight;
    private final int springOnionWeight;

    public static class Builder {
        // required fields
        private final int riceWeight;
        private final int waterWeight;
        private final int chickenWeight;

        // optional fields - default to 0
        private int saltWeight = 0;
        private int gingerWeight = 0;
        private int springOnionWeight = 0;

        public Builder(int riceWeight, int waterWeight, int chickenWeight) {
            this.riceWeight = riceWeight;
            this.waterWeight = waterWeight;
            this.chickenWeight = chickenWeight;
        }

        public Builder saltWeight(int val) {
            saltWeight = val;
            return this;
        }

        public Builder gingerWeight(int val) {
            gingerWeight = val;
            return this;
        }

        public Builder springOnionWeight(int val) {
            springOnionWeight = val;
            return this;
        }

        public ChickenCongeeRecipe build() {
            return new ChickenCongeeRecipe(this);
        }
    }

    private ChickenCongeeRecipe(Builder builder) {
        riceWeight = builder.riceWeight;
        waterWeight = builder.waterWeight;
        chickenWeight = builder.chickenWeight;
        saltWeight = builder.saltWeight;
        gingerWeight = builder.gingerWeight;
        springOnionWeight = builder.springOnionWeight;
    }
}
Enter fullscreen mode Exit fullscreen mode

And a client can create an object using

ChickenCongeeRecipe chickenCongee = new ChickenCongeeRecipe.Builder(100, 800, 50).saltWeight(6).gingerWeight(20).springOnionWeight(20).build();
Enter fullscreen mode Exit fullscreen mode

The object created is typically immutable.

Conclusion

A builder has its downside too. It's more expensive comparing to constructors or static factory methods. It could be a problem in performance-critical applications. To make it worthwhile, the parameter list should probably be bigger than four. Having said that, if we start with constructors and additional fields need to be added in the future, the constructor is going to stick around.

Top comments (0)