Knock Down The Builder Pattern

hqdefault

I want to stop people defaulting to the builder pattern. Before I do that, I should make a case for the builder pattern in question. All of the following are hugely important, when they’re important, and the builder pattern solves them.

Immutable Objects

If your data objects must be immutable after they are created, this means you can only create them with:

  • Constructors
  • Factory methods

The builder provides an abstraction over these so you don’t need to have varieties of constructor or factory method. This is because…

Missing Values

In the case where some use cases don’t have a particular value to provide at construction, the object needs to have a stable state. When the object has to be created in one go, the construction of the object needs to default those values. This can be done by telescoping methods – this is where there’s a variety of constructors or factory methods, with more and more inputs, overloading each other, probably called by the methods with fewer inputs, providing all possible variants of input. This can get complex and error prone quickly.

Builders allow you to express the values you have and then, when you call build() the right defaulting etc should occur as the object is created from the state of the builder.

Named Parameters

Compare the following:

Context context = new Context("/home", 8080, "en");

// vs

Context context = new Context.Builder()
  .path("/home")
  .port(8080)
  .language("en")
  .build();

Clearly the builder annotates the values. It seems extremely nice. It’s also long-winded… but attractive nonetheless.

Fluent Setters

The fact that there’s a fluent pattern within most builders, avoiding the repeated naming of the target object on each line makes them easier to read.

DSL-like construction

One of the main advantages of a builder pattern is that it validates the inputs and controls the state of the thing being built BEFORE you go and use it. If you look at this as a domain specific language, which you can use to describe something declaratively before it’s used imperatively, especially in cases where the builder uses more complex sub-builders to make inputs, you can end up with a very clear piece of construction code, which is more mistake proof at both runtime and compile time.

Look at how things like REST Assured construct assertions. You could argue that this is a combination of builders and DSL to make it both readable and semantically hard to get wrong.

    <span id="mce_SELREST_start" style="overflow:hidden;line-height:0;">&#65279;</span>when().
            get("/lotto/{id}", 5).
    then().
            statusCode(200).
            body("lotto.lottoId", equalTo(5),
                 "lotto.winners.winnerId", containsOnly(23, 54));

So what’s wrong with builders?

If they achieve so much, what’s wrong with builders?

  • They’re not the simplest solution – they add a whole extra class, which in trivial cases is a clone of the object being created.
  • In trivial cases, the effort of invoking a builder seems enormously over-wrought, compared with calling a constructor or factory method.
  • Dividing the logic of handling of default values between a value object and its builder can be (and often is) muddy.
  • Once you embrace builders for everything, you feel you OUGHT to use Lombok to implement them.

Lombok is a sign of failure

I’ve yet to look at a project with Lombok in it and think – well, that’s made everything easier. Lombok makes boilerplate easier at initial typing time. It hides the implementation details – the simple implementation details that don’t really need hiding – behind annotations. Your IDE cannot easily navigate the resulting code (finding usages etc) or highlight unused code. It makes it easy to quietly generate inaccurate code bloat.

But it makes builders easier.

And if we didn’t need builders for trivial examples, we wouldn’t festoon our code with the annotations turning it into a Lombok swamp.

I’m yet to find a really valuable builder that Lombok can make for us.

Stop using Lombok. Seriously. Or maybe minimise it a bit. See also here.

So…

The builder pattern, which is larger than just the creation of one type of concrete object, is enormously valuable. Over complicating simple object creation with Lombok is enormously perverse. Finding a middle ground is a good idea.

Java should probably embrace something closer to .NET’s property syntax, which would reduce the need for Lombok’s offering of @Getter/@Setter but autogenerating getters and setters by IDE is trivial and enables you to write… wait for it… BLOOMING JAVADOC!!!

When writing a mutable object, consider creating fluent setters, which both set a value and return this so you can chain them.

When composing your objects/implementation, think carefully about their structure so you can make best use of all techniques, rather than default to an overload of builders.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s