Wake Up From a Constructor Overload Nightmare With Java Named Params
Flite’s Java back-end uses a lot of Enums. Most of them are used for the purpose Enums were originally designed for – simple lists of settings and options. However a growing number of them are now being tasked with having each member carry a lot of metadata about its functionality.
At first this was not a big problem, but as the code evolved, more and more pieces of metadata were added to Enums with hundreds of members. Developers, not wanting to edit every single one of them to accommodate a new parameter, just added more and more constructors.
For example, this is a small part of a 100+ member Enum that defines our reporting columns
1 2 3 4
Four Enum members, four different constructors – none of which are direct extensions of another. In fact, this particular Enum had eight constructors, four of which were mysteriously marked as @Deprecated. Faced with adding yet another piece of metadata to this mess, I decided to look for a better answer.
I decided to try named params. There are a number of articles and blog posts out there on the topic. The most common suggestion is using a Builder pattern, which doesn’t work when constructing an Enum. Fortunately, there’s another way.
The goal is to be able to give each Enum member an arbitrary number of options, in any order, with the simplest and easiest to read and to maintain code. To do this, you need to create four simple pieces:
1) A private inner Enum within your Enum to list your option types. This contains every type of option you will want to pass to set up your Enum. For our Column Enum, it looks like this:
1 2 3 4 5 6 7 8
2) A private static inner class within your Enum to be the common object that can stand for any option. This is ultimately what your Enum constructor takes as its varargs parameters. If you’re doing something really fancy, I could see this class having more than just two members, but in our relatively straightforward case it’s a really simple class:
1 2 3 4 5 6 7 8 9
3) A static method for each option type. These methods create your options, and push down the actual implementation out of your Enum constructors, leaving them easier to understand. In our example they are really simple, but you could easily use these methods to do some input validation as well:
1 2 3 4 5 6 7 8 9 10 11 12
4) A single constructor that accepts a variable number of option types. This is the piece that ties everything together into a neat package. This is where you set all of your Enum’s private fields, and where you can do validation on the parameter set as a whole – for example enforcing that type and format were specified.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
So what does all of this code buy you? After a little refactoring, your Enum now looks like this:
1 2 3 4
A little longer than the original, but now far more readable. No more trying to figure out just which constructor is being used, what the 5th String parameter means, but only if the 4th parameter is a Long, and so on.
Best of all – adding a new option type requires you to add small pieces to the four components outlined above – surely a non-zero amount of work, but far simpler than refactoring a giant Enum to update each member’s constructor or descending even deeper into overload hell by creating yet another constructor with its own set of parameters.