Saturday, July 11, 2009

Introducing the Builder Design Pattern

Static factories and constructors have limitations when dealing with objects with large numbers of optional parameters.

A classic solution is to use multiple constructors. The first constructor will have only the required parameters. The second one, the required parameters and a single optional parameter. The third one, the required parameters and two optional parameter and so one until the last optional parameter. The problem with this solution is that you can easily invert two parameters when constructing the object.

Another solution is to use the JavaBean pattern, in which you call the paramterless constructor and call the setter methods to populate your objects. The problem with this pattern is that you cannot enforce consistency. Your objects may be in an inconsistent state if you do not set the required parameters.

A third solution is to use the builder design pattern.

Here's an interesting implementation of this design pattern described by Joshua Bloch in the book 'Effective Java Second Edition' (which is, by the way, a book to put in every programmer hands).

The client calls a constructor with all the required parameters and gets a builder object. Then the client calls methods on the builder object to set each optional parameters. Finally the client calls a build method which generate an instance of the object which is immutable. Immutable objects have a lots of benefits and may be very useful.

public class Customer {

private final String name;
private final String surname;
private final int age;
private final String address;
private final String email;

public static class Builder {

//Mandatory parameters
private String name;
private String surname;

//Optional parameters
private int age;
private String address;
private String email;

public Builder(String name, String surname) {
this.name = name;
this.surname = surname;
}

public Builder age(int val) {
age = val;
return this;
}

public Builder address(String val) {
address = val;
return this;
}

public Builder email(String val) {
email = val;
return this;
}

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

private Customer(Builder builder) {
name = builder.name;
surname = builder.surname;
age = builder.age;
address = builder.address;
email = builder.email;
}
}

A good practice is to check the invariants in the build method and send an IllegalStateException if one of the attribute is invalid. This way, you will always be sure that your object is valid after being instantiated.

Here's how the client code looks :

Customer customer = new Customer.Builder("John", "Doe").age(25).email("johndoe@gmail.com").build();

The result is a client code easy to write and read.

Resources : To mutate or not to mutate ?