Nicolas Frankel is an IT consultant with 10 years experience in Java / JEE environments. He likes his job so much he writes technical articles on his blog and reviews technical books in his spare time. He also tries to find other geeks like him in universities, as a part-time lecturer. Nicolas is a DZone MVB and is not an employee of DZone and has posted 217 posts at DZone. You can read more from them at their website. View Full User Profile

A Dive into the Builder Pattern

10.06.2013
| 14201 views |
  • submit to reddit

The Builder pattern has been described in the Gang of Four “Design Patterns” book:

The builder pattern is a design pattern that allows for the step-by-step creation of complex objects using the correct sequence of actions. The construction is controlled by a director object that only needs to know the type of object it is to create.


A common implementation of using the Builder pattern is to have a fluent interface, with the following caller code:

Person person = new PersonBuilder().withFirstName("John").withLastName("Doe") .withTitle(Title.MR).build();
This code snippet can be enabled by the following builder:

public class PersonBuilder {
 
    private Person person = new Person();
 
    public PersonBuilder withFirstName(String firstName) {
 
        person.setFirstName(firstName);
 
        return this;
    }
 
    // Other methods along the same model
    // ...
 
    public Person build() {
 
        return person;
    }
}

The job of the Builder is achieved: the Person instance is well-encapsulated and only the build() method finally returns the built instance. This is usually where most articles stop, pretending to have covered the subject. Unfortunately, some cases may arise that need deeper work.

Let’s say we need some validation handling the final Person instance, e.g. the lastName attribute is to be mandatory. To provide this, we could easily check if the attribute is null in the build() method and throws an exception accordingly.

public Person build() {
 
    if (lastName == null) {
 
        throw new IllegalStateException("Last name cannot be null");
    }
 
    return person;
}
Sure, this resolves our problem. Unfortunately, this check happens at runtime, as developers calling our code will find (much to their chagrin). To go the way to true DSL, we have to update our design – a lot. We should enforce the following caller code:

Person person1 = new PersonBuilder().withFirstName("John").withLastName("Doe").withTitle(Title.MR).build(); // OK
 
Person person2 = new PersonBuilder().withFirstName("John").withTitle(Title.MR).build(); // Doesn't compile

We have to update our builder so that it may either return itself, or an invalid builder that lacks the build() method as in the following diagram. Note the first PersonBuilder class is kept as the entry-point for the calling code doesn’t have to cope with Valid-/InvaliPersonBuilder if it doesn’t want to.


This may translate into the following code:

public class PersonBuilder {
 
    private Person person = new Person();
 
    public InvalidPersonBuilder withFirstName(String firstName) {
 
        person.setFirstName(firstName);
 
        return new InvalidPersonBuilder(person);
    }
 
    public ValidPersonBuilder withLastName(String lastName) {
 
        person.setLastName(lastName);
 
        return new ValidPersonBuilder(person);
    }
 
    // Other methods, but NO build() methods
}
 
public class InvalidPersonBuilder {
 
    private Person person;
 
    public InvalidPersonBuilder(Person person) {
 
        this.person = person;
    }
 
    public InvalidPersonBuilder withFirstName(String firstName) {
 
        person.setFirstName(firstName);
 
        return this;
    }
 
    public ValidPersonBuilder withLastName(String lastName) {
 
        person.setLastName(lastName);
 
        return new ValidPersonBuilder(person);
    }
 
    // Other methods, but NO build() methods
}
 
public class ValidPersonBuilder {
 
    private Person person;
 
    public ValidPersonBuilder(Person person) {
 
        this.person = person;
    }
 
    public ValidPersonBuilder withFirstName(String firstName) {
 
        person.setFirstName(firstName);
 
        return this;
    }
 
    // Other methods
 
    // Look, ma! I can build
    public Person build() {
 
        return person;
    }
}

This is a huge improvement, as now developers can know at compile-time their built object is invalid.

The next step is to imagine more complex use-case:

  1. Builder methods have to be called in a certain order. For example, a house should have foundations, a frame and a roof. Building the frame requires having built foundations, as building the roof requires the frame.
  2. Even more complex, some steps are dependent on previous steps (e.g. having a flat roof is only possible with a concrete frame)

The exercise is left to interested readers. Links to proposed implementations welcome in comments.

There’s one flaw with our design: just calling the setLastName() method is enough to qualify our builder as valid, so passing null defeats our design purpose. Checking for null value at runtime wouldn’t be enough for our compile-time strategy. The Scala language features may leverage an enhancement to this design called the type-safe builder pattern.

Summary

  1. In real-life software, the builder pattern is not so easy to implement as quick examples found here and there
  2. Less is more: create an easy-to-use DSL is (very) hard
  3. Scala makes it easier for complex builder implementation’s designers than Java

Published at DZone with permission of Nicolas Frankel, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Robert Saulnier replied on Mon, 2013/10/07 - 7:07am

Seems overly complex.

If you have required fields, like last name in your example, just add last name as a param to the Builder constructor.

public PersonBuilder {

    final String lastName;
    //other required fields

    public PersonBuilder(String lastName/*, other required fields*/) {
        this.lastName = lastName;
    }

    //...

    public Person build() {
        return new Person(this);

    }
}


matt inger replied on Mon, 2013/10/07 - 5:12pm

Agreed Robert. The above style of code would be nightmare to maintain, especially for applications which have a lot of domain objects.  It becomes busy work to maintain them instead of concentrating on the actual part of your code that does something.


Plus you're trying to enforce the order in which properties are supplied, which seems to be a bit too contrived in most cases.

Nicolas Frankel replied on Tue, 2013/10/08 - 1:19am in response to: Robert Saulnier

 Hello Robert,

This is adequate, until you have parameters dependent on one another, like in the house example.

Pieter van der Meer replied on Tue, 2013/10/08 - 3:37am

Nicolas,

I dont your recent comment to Robert holds. If the parameters are dependent on each other you have a different issue. Then its all about modeling your domain. In the house example I would choose for multiple domain objects i.e. the roof, foundation...

Further more I use the following library to create my builders, builder annotations with a few annotations added to the interface I get the builder pattern implementation classes as defined by Joshua Bloch. 

Works like a charm :-)

Nicolas Frankel replied on Tue, 2013/10/08 - 4:08am in response to: Pieter van der Meer

Site must be down :-(

Pieter van der Meer replied on Tue, 2013/10/08 - 6:40am in response to: Nicolas Frankel

should be up and running now :-( or :-)?

Rafik Hamid replied on Wed, 2013/11/27 - 3:02pm

Nice article, I agree that most articles discussing Builder pattern do not handle data validation before the build nor method call ordering.

That being said, I think this kind of builders is not productive as it takes too much effort and time to implement and maintain, in your example there is about 30 lines of code to build a class with 2 attributes, can you imagine how many lines of code it would take to create a Person in real life projects ?!!

Besides, I see the person is not immutable, so the developer could easily overlook the builder and use the "new" to create persons. However, the code could be simplified by removing withFirstName() from InvalidPersonBuilder and withLastName() from ValidPersonBuilder, this way we only have the remaining attributes to provide.

Amanpreet Khurana replied on Tue, 2014/03/11 - 2:12am

 If we have more than one mandatory fields?

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.