The Builder Pattern: Wrangling Wild Classes

Builder Pattern

Introduction

The builder pattern is an object creation design pattern that I have come quite fond of in Object Oriented Programming, specifically with Java code (though this is a perfectly good pattern for C++, C#, etc). The main principal behind the builder pattern is that it avoids the telescoping constructor anti-pattern by using a dedicated class (the builder) to handle object creation.

The telescoping constructor anti-pattern describes the situation where the required combinations of constructor arguments lead to an exponential increase in number of needed constructors. No doubt this is an issue that you have bound to come across if you have worked with complex objects, I certainly have had to battle with this as code bases progress.
*Note that this is a common problem with the Factory pattern

Telescoping Anti-Pattern

Let’s look at an example of the telescoping anti-pattern and why it should be considered an anti-pattern. A classic example we can begin to use is that of a User, since most web applications have some representation of a user.

You might start off building your user like so:

package com.jyore.example.security;

public class User {

    private String username;
    private String password;

    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    /* Getters/Setters snipped for brevity */
}

At this point, our POJO is quite simple and really has no issues. However, let’s say we rethought our user registration and decided that we should also capture the user’s email and optionally capture their first and last name. So now we have something like:

package com.jyore.example.security;

public class User {

    private String username;
    private String password;
    private String email;
    private String firstName;
    private String lastName;


    public User() {
    }

    public User(String username, String password, String email) {
        this.username = username;
        this.password = password;
        this.email = email;
    }

    public User(String username, String password,String email, String firstName, String lastName) {
        this.username = username;
        this.password = password;
        this.email = email;
        this.firstName = firstName;
        this.lastName = lastName;
    }

    /* Getters/Setters snipped for brevity */
}

Still not terrible. But what happens as the user profile requirements grow, especially the optional requirements? It is clear that our object’s constructor count will grow to handle multiple cases. Because of this, some might just provide a single default constructor or a basic, required constructor, and a fully loaded constructor (much like the one above).

The problem with going this route is that undoubtedly, you will meet many cases where you need one of the intermediary constructors that do not exist and will either need to add the additional constructor, or use a ton of setter methods.

Say we added an address and phone number to our user object where the address is a class containing all the typical information (line1,line2,city,state,zipcode) and the phone is just a string. We then modify the fully-loaded constructor to support adding these parameters as well, so we end up with something like this.

...
    public User(String username, String password,String email, String firstName, String lastName, String phone, Address address) {
        this.username = username;
        this.password = password;
        this.email = email;
        this.firstName = firstName;
        this.lastName = lastName;
        this.phone = phone;
        this.address = address;
    }
...

And now, if we needed to set everything except for the address, we are stuck. We either bite the bullet and add the constructor, or use our requirement-only constructor and then call setters.

    User user = new User("jyore","password","jyore@example.com");
    user.setFirstName("Joey");
    user.setLastName("Yore");
    user.setPhone("123-456-7890");

This not only looks ugly, but presents another issue regarding data integrity. Since the User object is mutable, we can use the setters and modify the data, naturally. But do we really want all parts of the application to have such power? Perhaps not.

In this case, let’s say we want to make our user object immutable. We add some final declarations to our attributes and remove all setters.

package com.jyore.example.security;

public class User {

    private final String username;
    private final String password;
    private final String email;
    private final String firstName;
    private final String lastName;
    private final String phone;
    private final Address address;


    public User() {
        this.username = null;
        this.password = null;
        this.email = null;
        this.firstName = null;
        this.lastName = null;
        this.phone = null;
        this.address = null;
    }

    public User(String username, String password, String email) {
        this.username = username;
        this.password = password;
        this.email = email;
        this.firstName = null;
        this.lastName = null;
        this.phone = null;
        this.address = null;
    }

    public User(String username, String password,String email, String firstName, String lastName, String phone, Address address) {
        this.username = username;
        this.password = password;
        this.email = email;
        this.firstName = firstName;
        this.lastName = lastName;
        this.phone = phone;
        this.address = address;
    }
}

And our previous code creating the object with setters now breaks and we are stuck adding the constructor.

...
    public User(String username, String password,String email, String firstName, String lastName, String phone) {
        this.username = username;
        this.password = password;
        this.email = email;
        this.firstName = firstName;
        this.lastName = lastName;
        this.phone = phone;
        this.address = null;
    }
...

Alas, we are now stuck with multiple constructors and creating new ones exponentially as our object becomes more complex.

Enter the Builder Pattern

So, now that we have identified the problem, how can we solve it? As you might have started to guess and this sections nice bold header would indicate, we can use the builder pattern!

As mentioned in the introduction, the builder pattern solves the telescoping anti-pattern by offloading the object creation to some other class. The best way to describe this is through example, so let’s rebuild our User object from the previous section using the builder pattern, then analyze it in detail.

package com.jyore.example.security;

public class User {

    private final String username;
    private final String password;
    private final String email;
    private final String firstName;
    private final String lastName;
    private final String phone;
    private final Address address;

    private User(UserBuilder builder) {
        this.username = builder.username;
        this.password = builder.password;
        this.email = builder.email;
        this.firstName = builder.firstName;
        this.lastName = builder.lastName;
        this.phone = builder.phone;
        this.address = builder.address;
    }

    public String getUsername() {
        return this.username;
    }

    public String getPassword() {
        return this.password;
    }

    public String getEmail() {
        return this.email;
    }

    public String getFirstName() {
        return this.firstName;
    }

    public String getLastName() {
        return this.LastName;
    }

    public String getPhone() {
        return this.phone;
    }

    public Address getAddress() {
        return this.address;
    }    

    public static class UserBuilder { 

        private final String username;
        private final String password;
        private final String email;
        private String firstName = "";
        private String lastName = "";
        private String phone = "";
        private Address address = "";

        public UserBuilder(String username, String password, String email) {
            this.username = username;
            this.password = password;
            this.email = email;
        }

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

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

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

        public UserBuilder address(Address address) {
            this.address = address;
            return this;
        }

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

    }

There is a fair amount going on here, so let’s break it down section by section, starting with the User attributes.

...
    private final String username;
    private final String password;
    private final String email;
    private final String firstName;
    private final String lastName;
    private final String phone;
    private final Address address;
...

This section is pretty standard. We want our User object to be immutable, so we have been careful to make each property final.

Our User object needs a single constructor (shown below). This constructor simply takes attributes from the UserBuilder object (more on this soon) and copies it into its own fields.

...
    private User(UserBuilder builder) {
        this.username = builder.username;
        this.password = builder.password;
        this.email = builder.email;
        this.firstName = builder.firstName;
        this.lastName = builder.lastName;
        this.phone = builder.phone;
        this.address = builder.address;
    }
...

It is important to note that the constructor above is marked private…and no that is not a typo. We want any constructor (should only ever be a single one) on the object to be built to be private such that the builder object MUST be used in order to create the object.

Next we define our getters as we normally would. I do not think it is necessary to put another snippet of those :). We do not have any setters, of course, because our User object is immutable.

With our User defined, we now can start to look at the actual builder class. This is typically defined as a public static inner-class to the main class, as shown in the example.

...
    public static class UserBuilder { 
...

Since it is simple and mostly self explanatory, we’ll first look at the last method, the build method, which basically calls the private constructor from the User object with the current instance of the builder that is being used. If you remember from above, the User constructor will simply map the attributes of the builder class to the actual User class.

...
        public User build() {
            return new User(this);
        }
...

Now, let’s delve into what makes the builder pattern tick…starting with attributes.

...
        private final String username;
        private final String password;
        private final String email;
        private String firstName = "";
        private String lastName = "";
        private String phone = "";
        private Address address = new Address();
...

Notice some attributes are marked final and others are not. The required attributes should always be final as they will be used in the builder’s constructor, since they are always needed. Once they are set, they should never be changed after being set, either. The constructor is a pretty standard constructor.

...
        public UserBuilder(String username, String password, String email) {
            this.username = username;
            this.password = password;
            this.email = email;
        }
...

The optional attributes, are not final and also com accompanied with a default value, as well as special setters. The defaults in this example are unfortunately pretty boring, but can be extremely useful depending on what you are building. Some use cases I’ve found them to be useful is when my object has specific types and there is some understood default Another good example was when there is some interface with multiple implementations (say some hashing service) and there is some default hashing service that is used.

The special setters are defined below

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

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

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

        public UserBuilder address(Address address) {
            this.address = address;
            return this;
        }
...

I call these special setters because they sort of break traditional setter convention (in a good way for a good reason). First, they are not named like setAttributeName, but rather just attributeName. The also are not return type void, but they actually return the builder object, UserBuilder. In each of them, the builder object returned is this, or the builder we are currently working with.

By returning the builder object type (and the specific instance at that), we have implemented a fluent api, where we can chain all of the methods together. This gives us the power to create any number of construction operations without ever having to change or add to our code (demonstrated in the usage examples below).

/* required only */
User user = new UserBuilder("jyore","password","jyore@example.com").build();

/* let's add in the name this time too */
User user = new UserBuilder("jyore","password","jyore@example.com")
    .firstName("Joey")
    .lastName("Yore")
    .build()
;

/* ok, no name, but have a phone */
User user = new UserBuilder("jyore","password","jyore@example.com")
    .phone("123-456-7890")
    .build()
;

/* all but the phone */
User user = new UserBuilder("jyore","password","jyore@example.com")
    .firstName("Joey")
    .lastName("Yore")
    .address(someAddress)
    .build()
;

/* all of the things */
User user = new UserBuilder("jyore","password","jyore@example.com")
    .firstName("Joey")
    .lastName("Yore")
    .phone("123-456-7890")
    .address(someAddress)
    .build()
;

So, we can simply create any form of the User object, and we have a single constructor in the User class and a single constructor in the UserBuilder class. We have successfully defeated the telescoping constructor anti-pattern, and in style too! By leveraging the fluid api style, our code (at least in my opinion) looks much cleaner than mucking around with setters.

Conclusion

By using the Builder Pattern, we are able to successfully remove the concern of the telescoping constructor anti-pattern. We are provided a powerful fluent api that allows us to essentially dynamically declare our constructors to handle our optional parameters. We also have an immutable object that has been constructed, which is optimal for the types of objects that get passed around throughout the code base, preventing tampering and potentially head aches of tracking down offending code.

The builder pattern should be a tool in your toolbox, but it does not fit every need. I highly recommend these for classes that tend to be immutable and have many potential configurations.

Please feel free to leave comments and questions in the comment section below. Happy coding!

Share on Facebook+1Share on Twitter Share
Posted in Programming Tagged with: , , , , , , , , , ,

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">