Effective Java’s Builder pattern

Introduction

In the last post, I wrote about the Gang of Four (GoF) Builder Design Pattern. In this post, we will look at the builder pattern from the Effective Java by Joshua Bloch. The builder pattern is also called Joshua Bloch’s (Effective Java’s) builder pattern. It is commonly confused with the Gang of Four Builder Design pattern. 
The Effective Java’s Builder pattern provides a nice and a safe way to build or construct an object that has a lot of optional parameters or instance variables.

Building a Post Object

Let’s say we have a Post class with five properties. They are the post title, the post author, the date at which the author posted it, the number of words and characters in it. Among them only the post title and the post author are mandatory fields. The others are optional (either we default it to a pre-defined default value or don’t care and make it null).

public class Post {
    private String title;
    private String author;
    private String datePosed;
    private int numberOfWords;
    private int numberOfCharacters;
}

There are various ways to build or construct this object. We will look at them and see what problems each has. Finally, we will look at how using a builder will solve this elegantly.

Option 1 – Telescoping constructor

When using a telescoping constructor, there will be one constructor with only the mandatory parameters. There will be one constructor taking all mandatory parameters along with one optional field and one constructor with all mandatory parameters plus two optional fields and so on.

Applying it our Post class, it looks like:

public class Post {
    private String title;
    private String author;
    private String datePosed;
    private int numberOfWords;
    private int numberOfCharacters;

    public Post(String title, String author) {
        this(title, author, null);
    }

    public Post(String title, String author, String datePosted) {
        this(title, author, datePosted, 0);
    }

    public Post(String title, String author, String datePosted, int numberOfWords) {
        this(title, author, datePosted, numberOfWords, 0);
    }

    public Post(String title, String author, String datePosted, int numberOfWords, int numberOfCharacters) {
        this.title = title;
        this.author = author;
        this.datePosed = datePosted;
        this.numberOfWords = numberOfWords;
        this.numberOfCharacters = numberOfCharacters;
    }
}

The first constructor accepts the mandatory fields viz., the post title and the author name. The second constructor, in addition to the mandatory fields, takes the post date. The third and the fourth takes number of words and the number of characters in addition to their previous ones, respectively.

Each of the constructor delegates to the next in the chain by passing a default value for an optional parameter.

We can use this as shown below:

Constructing a post object with only the mandatory fields

Post post = new Post("blog-post", "javadevcentral");
Constructing a post object with the mandatory fields + the post date
post = new Post("blog-post", "javadevcentral", "23/02");
Building a post object all fields
post = new Post("blog-post", "javadevcentral", "23/02", 1000, 5000);
 

Disadvantages of the telescoping constructor

There are several issues with this approach

  1. The client code is not easy to read. There are multiple parameters of the same type and we have to look at the other side to determine what is what. In this example, just looking at the integers 1000 and 5000 does not help. We have to go into the Post class to find that one is the number of words and the another is the number of characters. This also poses a great risk of the client/caller swapping them, but still would pass compilation.
  2. We still have to pass optional fields in some cases. Example: What if you want to build a post object with the post name, author and the number of words? Still, you would have to pass the post date. If we have to support the client to pass any set or combination of the optional fields, we have to write one constructor for each of the combination of the optional fields. And, when adding one more optional field in the future, we have to write a lot of new constructors. This is clearly not feasible and maintainable.

Option 2 – The Javabeans pattern

In this option, there will be only one constructor that takes all mandatory fields. There will be multiple setters (mutators) to set the optional fields.

public class Post {
    private String title;
    private String author;
    private String datePosed;
    private int numberOfWords;
    private int numberOfCharacters;

    public Post(String title, String author) {
        this.title = title;
        this.author = author;
    }

    public void setDatePosed(String datePosed) {
        this.datePosed = datePosed;
    }

    public void setNumberOfWords(int numberOfWords) {
        this.numberOfWords = numberOfWords;
    }

    public void setNumberOfCharacters(int numberOfCharacters) {
        this.numberOfCharacters = numberOfCharacters;
    }
}

We have three setters to set the number of words, characters and the post date.

Post post = new Post("blog-post", "javadevcentral");
post.setDatePosed("20/02");
post.setNumberOfWords(1000);
post.setNumberOfCharacters(5000);

This is clearly more readable and maintainable. 

Disadvantages of the Javabeans pattern

The object can be in an inconsistent state halfway through construction. This is because the construction of the object is split across multiple lines. It is possible that we could attempt to use the partly-constructed object (especially in an multi-threaded application/environment).
One of the biggest disadvantage is that the Post class is now mutable. We should always strive to make our classes immutable unless we have a good reason to not to.See the benefits of an immutable class.

The Builder

The Builder approach discussed here is a form of the Gang of four builder pattern. 
First, we create a Builder object by passing the mandatory fields to its constructor. Second, we set the values for the optional fields using the setter like methods on the Builder class. Finally, once we have passed/set all the fields, we call a build method on the builder. This method builds the object and returns it. 

public class Post {
    private String title;
    private String author;
    private String datePosed;
    private int numberOfWords;
    private int numberOfCharacters;

    private Post(Builder builder) {
        this.title = builder.title;
        this.author = builder.author;
        this.datePosed = builder.datePosed;
        this.numberOfWords = builder.numberOfWords;
        this.numberOfCharacters = builder.numberOfCharacters;
    }

    public static class Builder {
        private String title;
        private String author;
        private String datePosed = null;
        private int numberOfWords = 0;
        private int numberOfCharacters = 0;

        public Builder(String title, String author) {
            this.title = title;
            this.author = author;
        }

        public Builder datePosted(String datePosed) {
            this.datePosed = datePosed;
            return this;
        }

        public Builder numberOfWords(int numberOfWords) {
            this.numberOfWords = numberOfWords;
            return this;
        }

        public Builder numberOfCharacters(int numberOfCharacters) {
            this.numberOfCharacters = numberOfCharacters;
            return this;
        }

        public Post build() {
            return new Post(this);
        }
    }
}
The Builder is a static nested class of the Post class. We declare the same set of fields that are there in the Post class. The Builder’s constructor takes the mandatory fields alone. We defined one method to set each of the optional fields. Note that these return the Builder instance itself. This is useful to chain the builder method calls (will be seen soon). At last, the build method builds and returns the Post object.
 
The Post class now has a single constructor that takes an instance of type Builder. It unpacks the fields from the passed Builder instance and assigns the instance variable of the Post object. Note that the constructor of the Post class is now private.
 
Post post = new Post.Builder("blog-post", "javadevcentral")
        .datePosted("23/02")
        .numberOfWords(1000)
        .numberOfCharacters(5000)
        .build();

Above, we have built a Post object by passing all the fields. Since each of the builder methods return the same builder instance, it allowed us to chain the method calls. This makes the client code readable and more elegant.

We could have also added the parameter validation/check to each of the builder methods. Example: When passed a negative number for the number of words, we can throw an unchecked exception.

See the difference between a checked and an unchecked exception

Reusing builders

A nice property of the builder is that we can reuse a builder object to build different objects with just one or few property values changed.


This is because the final object is only built when we call the build method. Hence, the state of the builder object (builder object with values populated) can be reused to change one or few values to create a different object.

Post.Builder postBuilder = new Post.Builder("blog-post", "javadevcentral");
post = postBuilder.datePosted("23/02")
        .numberOfWords(1000)
        .numberOfCharacters(5000)
        .build();

Let us say we want to build a post object that has the same values for all fields as above, except that the number of characters is 6000. We don’t want to set all the fields again as explained earlier. We can simply reuse the postBuilder

Post newPost = postBuilder.numberOfCharacters(6000)
        .build();

This creates a new Post object that has 6000 characters (and same values for the rest of the fields set on the builder).

Advantages and Disadvantages of the Effective Java’s Builder Pattern

The Effective Java’s builder pattern overcomes the problems of the two earlier discussed approaches (the telescoping constructor and the Javabeans). It results in readable code as the builder methods have a name. We create the final object only when we call the build method and hence we never have an object in a partly constructed state.
The disadvantage of the builder is that it is verbose as we have to write a lot of code.

Conclusion

In this post, we learnt about a variation of the Gang of Four’s Builder pattern – the Effective Java’s builder pattern (or Joshua Bloch’s builder pattern). It provides an elegant and a safe way to build an object with many optional fields.

If you haven’t already read the Gang of Four’s Builder pattern, do check it out.

References

  1. Effective Java by Joshua Bloch
  2. Item 2: Consider a builder when faced with many constructor parameters
  3. The Builder Pattern and the Telescoping Constructor anti-pattern

Leave a Reply