Entries in design patterns (1)

Friday
Aug302013

Builder Pattern Instead of Error-Prone Constructors

When designing your classes, rather than following the path of the unreadable telescoping constructor, or leaving yourself open for bugs where the caller incorrectly passes a value into the wrong parameter because you have several of the same type, consider the following Builder pattern.

public class Person
{
    // required
    private String firstName;

    // required
    private String lastName;

    // optional
    private String url;

    // private constructor to defer responsibility to Builder.
    private Person()
    {
    }

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

    public void setFirstName(final String firstName)
    {
        if(StringUtils.isBlank(firstName))
        {
            throw new IllegalArgumentException("Person.firstName must not be blank");
        }
        this.firstName = firstName;
    }

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

    public void setLastName(final String lastName)
    {
        if(StringUtils.isBlank(lastName))
        {
            throw new IllegalArgumentException("Person.lastName must not be blank");
        }
        this.lastName = lastName;
    }

    public String getUrl()
    {
        return this.url;
    }

    public void setUrl(final String url)
    {
        this.url = url;
    }

    // builder class to ensure that all required fields are set while avoiding clunky, "telescopic" constructors
    public static class Builder
    {
        private Person built;

        public Builder()
        {
            built = new Person();
        }

        public Builder setFirstName(final String firstName)
        {
            built.setFirstName(firstName);
            return this;
        }

        public Builder setLastName(final String lastName)
        {
            built.setLastName(lastName);
            return this;
        }

        public Person build()
        {
            if(built.firstName == null)
            {
                throw new IllegalStateException("Person.firstName is required");
            }
            if(built.lastName == null)
            {
                throw new IllegalStateException("Person.lastName is required");
            }
            return built;
        }
    }
}

Having a private constructor will prevent anyone but the Person's Builder from instantiating a Person. The setters in the Builder return the Builder, which allows method chaining, providing a DSL-like self-documenting interface. The Builder's build() method is responsible for making sure that all properties are set before returning the Person.

Of course, this is a simple example with only two fields, so the alternative isn't exactly error prone:

Person blogger = new Person("Blake", "Caldwell");
blogger.setUrl("http://blakecaldwell.org");

But, once you add a few more required fields, the following makes your code easier to follow, while still ensuring all required fields are set.

Person blogger = new Person.Builder()
        .setFirstName("Blake")
        .setLastName("Caldwell")
        .build();
blogger.setUrl("http://blakecaldwell.org");

I first came across this pattern via a post by Petri Kainulainen. Check out his blog for great posts about Spring Data JPA and other topics.