The Safe Builder Pattern
As you may have read in a previous post called Abuse of the Builder Pattern, there are some problems with the widespread adoption of the Builder pattern as the most popular way to create large or complex objects which may have optional fields. The one I highlighted most was that it doesn’t actually give you any guarantees at compile time that your object is fully specified, which could lead to - at best - runtime errors on creation, or - at worst - silent unexpected non-initialized values or NullPointerExceptions.
Here I present a possible solution for getting round this problem in situation where (a) you are convinced that a Builder is the nicest API for what you want to do (b) you want a compile-time guarantee that the object is fully-specified and (c) you don’t mind putting in a little extra work and code to make it happen. It’s inspired partially by the
safeBuild idiom in Twitter’s Finagle project, but with extra use of Java 8 Lambdas to reduce the complexity for the user. The final result looks a bit like this:
Note that this article is less dogmatic than some of my previous ones, as I will not attempt to convince you that doing this is a good tradeoff of effort vs. reward. I’m exploring it for its academic value, not for its practical usefulness. (edit: I had to put this in bold because people just didn’t read this paragraph)
The concept behind this approach is that you exploit the Java compiler’s handling of type arguments, but instead of using those type arguments elsewhere in the functioning part of your code, you instead use them as flags to denote whether each required part of your Builder has been specified yet.
Let’s take the example from the previous post, but split the required field into two because it helps with the demonstration.
Next we need some types to use as flags. We could use any types here, but for the sake of exposing an acceptable API to the user, let’s create some new ones:
Then we need to parameterise the Builder with as many type arguments as required fields so that we can flip the correct type argument from N to Y when the field is specified. Here we have 2 required fields: “firstName” and “lastName”, so we need two type arguments for the builder.
Then we can alter the
setLastName methods to return altered versions of the type signature (remember we aren’t actually using these type arguments so the cast here is safe).
Now we know that if we start with a
Builder<N, N>, it will be safe to build the object when we have a
Builder<Y, Y>, because that means that both fields are specified.
However, we have no mechanism to declare a method on the builder that can only be used when it has certain type parameters, so we must declare it outside the Builder itself. Also, we need somewhere to get our Builder<N, N> from without having to explain this whole setup.
Let’s think about this in a functional way. What we actually need the user to provide is a function for transforming a
Builder<N, N> to a
Builder<Y, Y>. If they give us that function then we can use it to construct that object with the correct fields specified. That way, the user doesn’t have to worry about creating a builder then building from it, but instead just transforming the one they are given until it is valid. Instead of having a “build” method on the Builder, we can either put a static method or an extra constructor on the Person class:
Let’s put all this together:
As I said before I haven’t made up my mind whether this is something that really warrants a real-world use case, but it’s fun nonetheless. If you do see it being used well out in the wild somewhere, let me know and maybe it’ll help me make up my mind. As always, my address for correspondence is email@example.com