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.

PrintView Printer Friendly Version

EmailEmail Article to Friend

References (14)

References allow you to track sources for this article, as well as articles that were written in response to this article.
  • Response
    Blake Caldwell - /dev/blake/blog - Builder Pattern Instead of Error-Prone Constructors
  • Response
    Blake Caldwell - /dev/blake/blog - Builder Pattern Instead of Error-Prone Constructors
  • Response
    Blake Caldwell - /dev/blake/blog - Builder Pattern Instead of Error-Prone Constructors
  • Response
    Blake Caldwell - /dev/blake/blog - Builder Pattern Instead of Error-Prone Constructors
  • Response
    Blake Caldwell - /dev/blake/blog - Builder Pattern Instead of Error-Prone Constructors
  • Response
    Response: additional reading
    Blake Caldwell - /dev/blake/blog - Builder Pattern Instead of Error-Prone Constructors
  • Response
    Blake Caldwell - /dev/blake/blog - Builder Pattern Instead of Error-Prone Constructors
  • Response
    Response: nba 2K15 codes
    Blake Caldwell - /dev/blake/blog - Builder Pattern Instead of Error-Prone Constructors
  • Response
    Blake Caldwell - /dev/blake/blog - Builder Pattern Instead of Error-Prone Constructors
  • Response
    Blake Caldwell - /dev/blake/blog - Builder Pattern Instead of Error-Prone Constructors
  • Response
    Blake Caldwell - /dev/blake/blog - Builder Pattern Instead of Error-Prone Constructors
  • Response
    Blake Caldwell - /dev/blake/blog - Builder Pattern Instead of Error-Prone Constructors
  • Response
    Blake Caldwell - /dev/blake/blog - Builder Pattern Instead of Error-Prone Constructors
  • Response
    Blake Caldwell - /dev/blake/blog - Builder Pattern Instead of Error-Prone Constructors

Reader Comments (1)

Hi Blake,

First, I want to thank you for a great blog post (and providing link to my blog).

As I mentioned at Twitter, I have ran into some "problems" with this pattern when the number of required fields grows bigger than 3 (this is just something which I made up in my head). The reason for this is that I used to add the required fields as a constructor parameter to the builder:


Person visitor = new Person.Builder("Petri", "Kainulainen").build();

Before I read this blog post, I was thinking that solution to this problems would be to add a private validation method to the builder which ensures that the object is valid before it is created. This would give me the possibility to

1) Set the values of "some" required properties by using methods of the Builder class.
2) Leave "some" required properties as constructor arguments.

Your post made me realize that even though this kind of validation might be required, it does not solve the problem I have with constructor arguments. The problem with constructor arguments (in this case) is that it is really hard to "add meaning" to them. If you set all properties by using the methods of the builder class, each method name can give business meaning to the action. This is a great lesson.

By the way, if you are interested of expanding this approach to tests as well, you might want to check out this wiki page. It explains how you can extend Fest Assert and use DSL like assertions in your tests. I have started experimenting with this approach and it looks way better than using the standard JUnit assertions.

August 30, 2013 | Unregistered CommenterPetri Kainulainen

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>
« Object/Relational Mapping: Know Your Frameworks | Main | Mouse Issues on Mac OS X 10.8 [SOLVED] »