Core Java

Builder As A (Fail-Fast) State Machine

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.

Builder State Machine
Tom & Jerry – Mouse Trouble, by William Hanna and Joseph Barbera

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:

public interface DateBuilder {

    DateBuilder addDay(final Integer day);
    DateBuilder addMonth(final Integer month);
    DateBuilder addYear(final Integer year);
    DateBuilder addSeparator(final String sep);

    String build();

}

The interface above will have five implementations: StringDateBuilder (the public entry point), ExpectSeparator, ExpectMonth, ExpectYear and ExpectBuild (these four are package protected, invisible to the user). StringDataBuilder looks like this:

public final class StringDateBuilder implements DateBuilder {

    private final StringBuilder date = new StringBuilder();

    @Override
    public DateBuilder addDay(final Integer day) {
      this.date.append(String.valueOf(day));
      return new ExpectSeparator(this.date);
    }

    @Override
    public DateBuilder addMonth(final Integer month) {
      throw new UnsupportedOperationException(
        "A day is expected first! Use #addDay!"
      );
    }

    @Override
    public DateBuilder addYear(final Integer year) {
      throw new UnsupportedOperationException(
        "A day is expected first! Use #addDay!"
      );      
    }

    @Override
    public DateBuilder addSeparator(final String sep) {
      throw new UnsupportedOperationException(
        "A day is expected first! Use #addDay!"
      );
    }

    @Override
    public String build() {
      throw new UnsupportedOperationException(
        "Nothing to build yet! Use #addDay!"
      );
    }

}

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 build().

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 addMonth or 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.

Published on Java Code Geeks with permission by MIhai Andronache, partner at our JCG program. See the original article here: Builder As A (Fail-Fast) State Machine

Opinions expressed by Java Code Geeks contributors are their own.

Mihai Andronache

Mihai is an experienced Java and JavaEE developer. His main interests are: clean OOP, code maintainability and testing. He is the creator of http://comdor.co and http://charles.amihaiemil.com"
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

4 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Y. Martin
Y. Martin
5 years ago

That is a good starting point. For dummies, I propose you to describe an AbstractDateBuilder where all methods throw UnsupportedOperationException and document at least first two implementations providing only addDay and then addMonth…

Tom
Tom
5 years ago

It is a pretty good solution, but regarding the UnsulportedOperationExceptions, I still prefer validating/controllong API usage in compile-time over runtime whenever possible.
APIs amount grows in an incredible pace and it’s way easier to learn a new one, when it simply disallows you making a mistake like calling unsupported methods. That’s just my point of view based on my personal experience, though.

Sajmon
Sajmon
5 years ago

I know its abstract example. But cant find real example for this case. I mean, there is always way to collect all string and concanate them in build. If it is clean builder pattern. No matter if object would be string or larger structure.
I am using fail fast in builder, but only when field is mandatory. And two chcecks, one in ‘withField’ and then in ‘build’.

Mihai A
5 years ago
Reply to  Sajmon

Yes, the example is obviously too abstract/simple, people didn’t like it too much. But here’s a concrete example (when you cannot change the Builder’s interface):

Reference Implementation of JSON-P’s JsonObject generator/builder: https://github.com/javaee/jsonp/blob/master/impl/src/main/java/org/glassfish/json/JsonGeneratorImpl.java

And here is my implementation of the same builder: https://github.com/amihaiemil/eo-jsonp-impl/blob/master/src/main/java/com/amihaiemil/eojsonp/RtJsonGenerator.java

Back to top button