Spot the difference

Readability of code should not only be pursued in production code. It is also very important to make your (unit)tests as readable as possible. One of my favourite patterns for enhancing readability is the builder pattern.

Take a look at the following code snippet of a unit test. As you will notice, the creation of the User object involves a lot of lines. Not also does it involve a lot of lines of code, it isn’t quite readable as I would like it to be.

    ...

    @Test
    public void not_return_any_tweets_when_user_is_not_followed_by_logged_in_user() throws UserNotLoggedInException {
        tweetService = new TweetService(PIERCE, tweetDAO);

        User james = new User();
        james.setName("James Bond")
        james.addFollower(ROGER);
        james.addFollower(TIMOTHY);
        james.addFollower(DANIEL);
        james.addTweet(ABOUT_ACTORS);
        james.addTweet(ABOUT_MOVIES);

        assertThat(tweetService.getTweetsByUser(james).size(),is(0));
    }

    ...

Don’t get me wrong, the code above is quite readable but if you take a look at the code below it reads a bit more nicer. It is almost like you are reading a normal sentence. That is what the builder pattern gives you. Fluent readable object creation.

    ...

    @Test
    public void not_return_any_tweets_when_user_is_not_followed_by_logged_in_user() throws UserNotLoggedInException {
        tweetService = new TweetService(PIERCE, tweetDAO);

        User james = aUser()
                        .named("James Bond") 
                        .followedBy(ROGER, TIMOTHY, DANIEL)
                        .withTweets(ABOUT_ACTORS, ABOUT_MOVIES)
                        .build();

        assertThat(tweetService.getTweetsByUser(james).size(),is(0));
    }

    ...

Note that the static import is used in order to call aUser() without the class name. Otherwise it would read Userbuilder.aUser(). The latter is also readable but the first one reads a bit more natural. (But that is nitpicking 2.0)

Getting it done

How does that pattern work, you might think. The basic idea is that while creating the object the the builder saves all the data and when all is ready the build() method is invoked and at that point the object is created.

In order to chain the methods together all the methods that collect data (e.g. named an followedBy) should return the Builder object itself.

Creating the builder contains three steps:

  • Creating the static method which returns a new Builder (opening method)
  • Creating the data collecting methods which return the builder (intermediate methods)
  • Creating the build method (terminating method)

Opening method

This static factory method (not to be confused with the Factory design pattern) is added pure for enhanced readability.
If we didn’t have this static method the builder would has to be invoked something like this: new UserBuilder().followedBy(…). The intent is less clearer as opposed to using the static factory method.

    ...

    public static UserBuilder aUser() {
        return new UserBuilder();
    }

    ...

Intermediate methods

In order to chain the methods each intermediate method should return itself. Notice the usage of the varargs parameter. By using the varags we delegate the adding of followers one-by-one to the builder itself. This is also the case for the withTweets(…) method.

    ...

    public UserBuilder followedBy(User... followers) {
        this.followers = followers;
        return this;
    }

    ...

Terminating method

For actually creating the User object, the builder must have a terminating method. This method is responsible for creating the actual object needed by the client. In this method we see that every piece of data, collected by the intermediate methods, is transferred to the User object and finally the constructed user is returned to the client.

    ...

    public User build() {
        User user = new User();
        setNameFor(user);
        addTweetsTo(user);
        addFollowersTo(user);
        return user;
    }

    ...

The full builder

public class UserBuilder {
    private String name = "";
    private User[] followers = new User[]{};
    private Tweet[] tweets = new Tweet[]{};

    public static UserBuilder aUser() {
        return new UserBuilder();
    }

    public UserBuilder named(String name) {
        this.name = name;
        return this;
    }

    public UserBuilder followedBy(User... followers) {
        this.followers = followers;
        return this;
    }

    public UserBuilder withTweets(Tweet... tweets) {
        this.tweets = tweets;
        return this;
    }

    public User build() {
        User user = new User();
        setNameFor(user);
        addTweetsTo(user);
        addFollowersTo(user);
        return user;
    }

    private void setNameFor(User user) {
        user.setName(name);
    }

    private void addFollowersTo(User user) {
        for (User follower : followers){
            user.addFollower(follower);
        }
    }

    private void addTweetsTo(User user) {
        for (Tweet tweet : tweets){
            user.addTweet(tweet);
        }
    }
}

Conclusion

According to Wikipedia “The intention of the builder pattern is to find a solution to the telescoping constructor anti-pattern that occurs when the increase of object constructor parameter combination leads to an exponential list of constructors. Instead of using numerous constructors, the builder pattern uses another object, a builder, that receives each initialization parameter step by step and then returns the resulting constructed object at once.

I would like to add that the builder pattern can also be used for enhanced readabilty and easier creation/validation of objects.