Java Design Patterns – Why Use a Builder Class in Builder Pattern?

builder-patterndesign-patternsjava

I have seen many implementations of the Builder pattern (mainly in Java). All of them have an entity class (let's say a Person class), and a builder class PersonBuilder. The builder "stacks" a variety of fields and returns a new Person with the arguments passed. Why do we explicitly need a builder class, instead of putting all the builder methods in the Person class itself?

For example:

class Person {

  private String name;
  private Integer age;

  public Person() {
  }

  Person withName(String name) {
    this.name = name;
    return this;
  }

  Person withAge(int age) {
    this.age = age;
    return this;
  }
}

I can simply say Person john = new Person().withName("John");

Why the need for a PersonBuilder class?

The only benefit I see, is we can declare the Person fields as final, thus ensuring immutability.

Best Answer

It's so you can be immutable AND simulate named parameters at the same time.

Person p = personBuilder
    .name("Arthur Dent")
    .age(42)
    .build()
;

That keeps your mitts off the person until it's state is set and, once set, won't let you change it, yet every field is clearly labeled. You can't do this with just one class in Java.

It looks like you're talking about Josh Blochs Builder Pattern. This should not be confused with the Gang of Four Builder Pattern. These are different beasts. They both solve construction problems, but in fairly different ways.

Of course you can construct your object without using another class. But then you have to choose. You lose either the ability to simulate named parameters in languages that don't have them (like Java) or you lose the ability to remain immutable throughout the objects lifetime.

Immutable example, has no names for parameters

Person p = new Person("Arthur Dent", 42);

Here you're building everything with a single simple constructor. This will let you stay immutable but you loose the simulation of named parameters. That gets hard to read with many parameters. Computers don't care but it's hard on the humans.

Simulated named parameter example with traditional setters. Not immutable.

Person p = new Person();
p.name("Arthur Dent");
p.age(42);

Here you're building everything with setters and are simulating named parameters but you're no longer immutable. Each use of a setter changes object state.

So what you get by adding the class is you can do both.

Validation can be performed in the build() if a runtime error for a missing age field is enough for you. You can upgrade that and enforce that age() is called with a compiler error. Just not with the Josh Bloch builder pattern.

For that you need an internal Domain Specific Language (iDSL).

This lets you demand that they call age() and name() before calling build(). But you can't do it just by returning this each time. Each thing that returns returns a different thing that forces you to call the next thing.

Use might look like this:

Person p = personBuilder
    .name("Arthur Dent")
    .age(42)
    .build()
;

But this:

Person p = personBuilder
    .age(42)
    .build()
;

causes a compiler error because age() is only valid to call on the type returned by name().

These iDSLs are extremely powerful (JOOQ or Java8 Streams for example) and are very nice to use, especially if you use an IDE with code completion, but they are a fair bit of work to set up. I'd recommend saving them for things that will have a fair bit of source code written against them.

Related Topic