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.