Distinguish Between Optional and Mandatory Parameters in the Builder Pattern
Takes 5 minutes to read
After reading through Designing Bulletproof Code by Otavio Santana I stumbled upon its example of using a builder pattern. While this was not the focus of the article itself I also realized that I saw the issue in the past a lot and I ran in it as well. The widely spread understanding of the builder pattern (as described in Effective Java by Joshua Bloch) does not differentiate between optional and mandatory parameters and that makes their usage not easier but harder.
Let’s start with an example. Imagine you want to use a immutable class called Account.
This class is absolutely user friendly to use. When you type new Account(<press autocomplete key> you’ll get an precise understanding which parameters are mandatory and which are not because you can see it by looking at the offered constructors.
While this is nice to handle with such a small amount of parameters it will get very nasty when you have a lot of optional parameters because you’d need to offer all combinations of optional parameters as constructors. So you’d also loose the good readability for the client in the first place.
Another reason to refactor this approach with many parameters is, that the constructors can be very large and, without going into details here, you should avoid any method to have more than (arbitrary number incoming…) 4 parameters.
For the sake of simplicity I’ll continue this example with adding just one more optional parameter called language.
So a well-known solution to make things simpler again is applying the builder pattern like in the Designing Bulletproof Code article. Doing that for our example would result in this:
With that code we maybe solved the issue of having too many parameters in the constructor but the usage is not easier now. Using auto-completion of your idea results in this:
So how should you know that
will produce a validation exception but
The answer is, you don’t. Except you look into the documentation or into the source code, but that’s tedious, isn’t it? So it would be more robust when the compiler could tell you in the first place how to construct a valid instance of our Account class. There are two solutions for this problem.
Construct Builder with Mandatory Parameters
One is, only allow the builder to be constructed by setting the mandatory parameters of the final object in the constructor of the Builder. Doing this for our example builder would look like that:
Now there is no way around of setting the correct parameters and understanding that all the others must be optional.
But we can run into the same problem that we wanted to avoid in the first place. With a lot of mandatory parameters our constructor will get large for the builder. I mean this time we’d arguable just have mandatory parameters in their and it might be okay, but a more readable solution would be nice anyway and here we come to the second (but even verboser…) approach.
Use Interfaces to Guide through the Builder
I stumbled upon this approach by reading Builder pattern with a twist by Uzi Landsmann. The idea is having interfaces that guide you through the process of creating a valid instance of the object. For our example it would look like this:
Chaining the different Interfaces now results in convenient usage for the client:
But as you can see this is muuuuuuuuch boilerplate we have to write here and I don’t know a library yet that can generate the necessary bytecode for this pattern yet. Maybe this is a nice project to build at some point.
As George Gastaldi pointed out. There is a neat library called Immutables who can generate staged builders for you. I will give it a try asap.
Congratulations for the article. Btw, @ImmutablesOrg helps A LOT in generating builders