Blake Caldwell: /dev/blog
Not a Java Developer

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.