As part of the API, Mark implementing method chaining. This allows for very compact code that is still quite easy to read. For example, you could produce this XML:
<?xml version="1.0" encoding="UTF-8"?>
<car year="2008">
<model>Prius</model>
</car>
with just this code::
WAX wax = new WAX(WAX.Version.V1_0);
wax.start("car").attr("year", 2008)
.start("model" ).text( "Prius").close();
Much of the compactness comes from the fact that each method called returns the WAX object, allowing the developer to chain method calls together.
As we were discussing the code, the topic of incorrect ordering of calls came up. For example, what happens if you try to call the attr method to create an attribute after already creating some text inside the element with the text method?
Because WAX is a streaming API, you can't 'go back' and add an attribute in the start element tag after having already 'moved on' to putting text in the body of the element. For example, what if I tried to create an attribute to the model of the car after putting text in it, like this:
WAX wax = new WAX(WAX.Version.V1_0);
wax.start("car").attr("year", 2008)
.start("model" ).text( "Prius").attr( "trim", "GT" ).close();
WAX detects this situation and throws an exception at runtime, which it should do. But I'm a fan of static type checking. I love having tools do as much checking for me as possible. So I suggested to Mark that he create a set of interfaces. Each interface would contain only the methods that make sense at the current point in the output stream. This would allow the compiler to catch invalid method invocations. Even better, modern IDEs like IDEA would flag the method invocation visually even before compilation.
For example, if we arrange for the text method to return an interface which does not have an attr method, then the compiler will catch it and the IDE will flag the error visually. For example, in IDEA, the error is flagged like this:
Now we get the compactness of runtime exceptions (which Mark really likes) and static checking (which I really like). I did some looking around and couldn't find a design pattern that really fits. A number of people suggested that this was an example of a domain-specific language (DSL). But to my thinking, something that deserves the moniker of 'language' should be more involved than simply constructing some interfaces so that static checking is applied.
But whatever you call it, I think it's a useful design pattern than can be applied in other situations to make for clean, compact, yet statically checked APIs.