Simple Implementation of Fluent Builder - Safe Alternative To Traditional Builder
Lets assume that we want to create a Fluent Builder for simple bean.
Sergiy Yevtushenko
The Builder pattern is extremely popular in Java applications. Unfortunately it's often misunderstood and incorrectly implemented and used which results to run time errors.
So, what to do in cases when object must be built completely and there are no safe defaults for fields (i.e. Builder pattern is not applicable)? In such cases would be very convenient to use Fluent Interface pattern which allows to avoid errors caused by missing field(s).
Unfortunately proper implementation of Fluent Interface usually is too verbose to be practical so developers use plain Builder and try to rely on careful testing to avoid runtime errors.
Fortunately for most frequent use case when all fields for the object must be set, there is a simple solution which results to very concise and safe implementation of Builder with fluent interface, which I call Fluent Builder.
Lets assume that we want to create a Fluent Builder for simple bean shown below:
In Java 14 such classes can be declared as records so necessary boilerplate code will be significantly reduced.
Lets add a Builder. First step is quite traditional:
Let's implement traditional builder first so it will be more clear how Fluent Builder code is derived. Traditional Builder class would look like this:
One important observation: every setter returns this and this in turn allows users of this call to invoke every method available in builder. This is the root of the issue, because user is allowed to call build() method prematurely, before all necessary fields are set.
In order to make Fluent Builder we need to limit possible choices to only allowed ones, therefore enforcing correct use of the builder. Since we're considering case when all fields need to be set, then at every building step only one method is available.
To do this we can return dedicated interfaces instead of this and let Builder implement all these interfaces:
Huh. Somewhat ugly and a lot of additional boilerplate. Can we do better? Let's try.
First step is to stop implementing interfaces and instead return anonymous classes which implement these interfaces:
This is much better. We again can safely return SimpleBeanBuilder from builder() method since this class exposes only one method and does not allow to build instance prematurely. But much more important is that we can omit whole setters and mutable fields boilerplate in the builder significantly reducing amount of code. This is possible because we creating anonymous classes in the scope where parameters of all setters are visible and accessible. And we can use these parameters directly without need to store them!
Resulting code looks is comparable to original Builder implementation in regard to total amount of code.
But we can do better! Since all anonymous classes are in fact implementation of the interfaces which contain only one method, we can replace anonymous classes with lambdas:
Resulting Fluent Builder contains even less boilerplate code than original builder!
But we can do even better! Notice that remaining SimpleBeanBuilder class is very similar to other builder interfaces, so we can replace it with lambda as well:
For those who isn't yet used to deeply nested lambdas this code might be harder to read. From the other hand, there is no need to write it manually as we can offload this task to IDE (just as we do with traditional builders).
Using this approach we can replace traditional Builders with Fluent Builders and get Builder convenience with Fluent Interface safety.
Upvote
Sergiy Yevtushenko

Related Articles