This is an idea that came to me a few weeks ago while designing a “Generator” class that had
to send the input to an encapsulated
Writer. It was, in fact, the Builder pattern. However,
the rules were a bit more complex, the user had to call the
add...() methods in a certain way, for the output to be generated correctly.
Needless to say, I didn’t like the option of having one single
BuilderImpl class that would set and verify all sorts of flags internally, in order to know what and when it was allowed to do. The solution was to build a Finite State Machine, since the builder’s interface was fluent. As usual, in this post I’ll illustrate it all with an example.
Let’s assume we want to implement a
DateBuilder that would generate a
String in the classic
dd.mm.yyyy format (maybe with other types of separators as well, not only
.). For the sake of simplicity, we’ll focus only on format and forget cases such as number of days in a month, leap years etc. First comes the interface:
The interface above will have five implementations:
StringDateBuilder (the public entry point),
ExpectBuild (these four are package protected, invisible to the user).
StringDataBuilder looks like this:
I’m sure you get the point already: the other four implementations will handle their own situations. For instance,
ExpectSeparator will throw an exception from all methods except
addSeparator(...), where it will append the separator to the
StringBuilder and return an instance of
ExpectMonth. Finally, the last node of this machine will be
ExpectBuild (returned by
ExpectYear after adding the year), which will throw exceptions from all methods besides
This design helped me keep my
code objects small, free of flags and
if/else forks. As usual, each of the classes above are easily tested and the builder’s behaviour is easily changeable by switching the returned implementations.
Of course, I am not the only one with these thoughts: mr. Nicolas Fränkel wrote about this very idea just last month here. However, I felt the need to bring my two cents because I did not like his example entirely: he used different interfaces for the builder’s nodes in an attempt to keep the builder safe and idiot-proof (e.g. don’t even allow the user to see an
build method if they shouldn’t use it). This is something I don’t agree with because it means even more code for me to manage and besides, the client will be more coupled with the builder’s logic. I’d rather just force the user into learning how to use the builder (it shouldn’t be a big effort for them, since they’re supposed to catch any exceptions with the simplest of unit tests, right? right…)
I found this article too, which offers a broader, more theoretical explanation, not necessarily tied to the Builder pattern – if you think about it, this approach could be used with any kind of object that has to change its behaviour based on its internal state.