/ BUILDER, DESIGN PATTERN, STATE MACHINE

The Builder pattern is a finite state machine!

Some months ago, I read the post referenced in this tweet:

In short, the article provides two ways to generate the boilerplate code required by the Builder pattern:

  1. Lombok, to generate code at compile time
  2. The Spark Eclipse plugin, to generate code at development time

However, I already wrote about the Builder pattern. And it’s a bit more complex than what’s described in the referenced post.

I gave it some thoughts, and I came up with 3 different levels of complexity in the Builder pattern. Only the first one can be managed by the above solutions.

Basic Builder

Let’s start with a simple example, a Person builder. The Person can have three arguments: a title, a first name and a last name. It’s a very contrived example, and doesn’t require a Builder, but it serves its purpose. Bear with me for the sake of explanation.

Let’s model this Builder as a finite state machine. There’s a single state, and withXXX() methods are transitions going from and to it:

A simple Builder

At this point, the Builder’s only reason for being is improve on the situation when a constructor has many arguments. IMHO, compared to other cases below, it’s a degenerate Builder, as the finite state machine only has one state.

Required and optional arguments

Now, let’s add an additional constraint: compared to the previous case, the last name argument is now required.

A possible option would be to add the argument to the builder constructor. While it’s possible for a small number of arguments, it soon becomes unmanageable if this number increases. Another alternative is use the finite state machine (again):

  1. The first state is designed around a builder without a build() method: a builder that is invalid
  2. The second state is designed around a builder with a build() method

And the only way to go from the first state to the second is by passing the first name argument.

A more complex builder

Hierarchical builders

The previous paragraph was about required and optional arguments. Let’s add one more level of complexity. Imagine a pizza builder, allowing to choose two different pizza layers: the sauce base, and the different ingredients.

However, some ingredients should only be available for specific bases. For example, it’s only possible to add pineapple on tomato base, while salmon can only be added to sour cream base.

I profusely apologize for my Italian readers who believe that putting pineapple on pizzas is a crime against Italian cuisine. This is only meant to illustrate my explanation.

The corresponding finite state machine can be modeled as such:

Hierarchical builders
Of course, additional requirements can then be added as states and transitions to the above.

Conclusion

As seen from the above, builders can handle complex use-cases. The simplest one - described in the first paragraph is an easy target for code generation, either at bytecode or at source level. Other cases described in the other two paragraphs are not so easily handled.

To generate code in a sufficiently generic way to handled every approach, I believe the best way is to model the Builder as a finite state machine.

Nicolas Fränkel

Nicolas Fränkel

Nicolas Fränkel is a Developer Advocate with 15+ years experience consulting for many different customers, in a wide range of contexts (such as telecoms, banking, insurances, large retail and public sector). Usually working on Java/Java EE and Spring technologies, but with narrower interests like Software Quality, Build Processes and Rich Internet Applications. Currently working for Exoscale. Also double as a teacher in universities and higher education schools, a trainer and triples as a book author.

Read More