Fixing Inheritance with Bloch's Builder
Fixing Inheritance with Bloch's Builder
Join the DZone community and get the full member experience.Join For Free
I love the static builder pattern Joshua Bloch unveiled in the second edition of Essential Java I use it all the bloody time. However, for entities that inherit from another complex entity, the pattern gets kind of ugly. I have blogged about this before. The short reason is that the fluent builder pattern works by chaining calls of methods that return the builder (hence the next dereference works) until you get to the terminal call, which is the build method that makes the actual instance. The problem with inheritance is that at some point, you are going to return a builder that is from the base class and you are going to try to invoke a method that's in the derived class, and you will get an error. The immediate thought is ‘well, if I use the methods from the derived first I should be ok, because when the last derived call returns the derived builder, the call to the method in the base will work, and then it will return the base.‘ The problem with that is that then the base return goes to the actual build method. I have just done this in the past with 2 different chains:
- make the builder, e.g. Builder builder = new Entity.Builder();
- call all the base class methods: builder.name(blah).created(new Date());
- call the derived methods and build: Entity e = builder.derivedProperty(x).build();
This is not so bad. It just kind of defeats the purpose and saps energy into some pretty silly requirements. (The refactor rule of thumb applies here: if you want to brush your teeth after explaining to someone why their attempt to use your stuff doesn‘t work, you are plying something that is clearly suboptimal.. :) )
This week, we were talking about this and I came across this blog post by Eamonn McManus . It‘s really interesting, and you have to give the dude serious props for trying so hard, but I am not real thrilled with the chosen solution: it means dragging around a lot of detritus. I went away from it after reading about it and came back and while there is code folding, I can‘t imagine repeating all this stuff in every bloody entity I am ever going to make.
I am starting to think that maybe for derived ones, I might just declare all the methods in the derived and then delegates to the base where appropriate. That is noise, but in a way it‘s at least good noise, and I have taken to folding up the builder anyway. I hate to suggest it, but probably the base class methods could be hoisted via the clipboard and then changed en masse pretty easily. If there were a way to really hide the generic shenanigans and the self stuff, I would be much more inclined in the direction of Eamonn‘s solution.
Meantime, this is one of those rare instances where I do see something of a lack in the language. Last time I mentioned that the use of init methods in Objective-C with the labels for the positional (not named) parameters ends up being great for readability. That‘s the main reason I like the builder in Java (and of course, in both languages, the fact that it solves the problem of how to achieve immutability with complex objects that require a lot of setup). Apparently, there was a proposal to have Java 7 treat void return values as a return of this, but that was rejected.
The utility of this pattern has continued to grow. Lots of people are now calling complete implementation of Fluent Builders DSLs. For instance, in the recent update to Hibernate Search, they unveiled a new query builder DSL which is basically just a Fluent Builder. That said, it‘s a cluster of builders, because it allows you to build components of the query then pull them together. I am doing something similar now for scraping pages. This is as/perhaps more exciting use of Builder because it makes you really focus on how to make your code readable, and how you are going to support the correct syntax is secondary (as it should be).
I have always been obsessed with the builder pattern. This is a variant of it that is very useful. The inability to fluidly configure the properties is a major shortcoming.
Opinions expressed by DZone contributors are their own.