I’m deviating a little from my XML anglebracket warfare today so let’s all rejoice. Done rejoicing yet? Too bad! I’m moving on. Today’s topic is about writing developer friendly code. Developer friendly code (yes, developer friendly code! How many times should I say it?) amounts to APIs that are actually fun to use. How many of you have ever used JMock? Raise your hand. (Put it down you fool! I can’t see your hand. Besides, you got people looking at you sideways.) Now how about the StringBuffer class? How many of you had fun chaining method calls together to build complicated dynamic Strings? What do these APIs have in common? What’s that you say about return values? Wrong! They’re all alike because you can build slick looking shapes and pictures in your editor as you use them. I once drew the Statue of Liberty with a slew of StringBuffer appends and clever indentation. Besides re-creating works of DaVinci, these APIs make you feel good because… well just because they’re just fun to use. I’m going nowhere with my discussion here so let me elaborate, specifically on JMock. When you write a unit test with JMock it almost reads like pure English. Those clever JMock developers did everything in there power to enforce English-like readability short of requiring you to use a comma between clauses. How did they do that? Well they chose clever method names like once() and will() and making sure that return values from certain methods can be passed into other methods in a way that it builds a sentence in english grammar. They really thought hard about readability and as a result you get to write:
throwException(new CountOutOfRangeException("Cannot count past 20")) ) );
And when you supplement that with comments you get:
mock.expects(/*a call*/ atLeastOnce()). /*to*/ method("updateCounter")
.with(/*a parameter*/eq( /*to our*/expectedIncrement))
/*and it*/.will( onConsecutiveCalls(
/*and finally*/throwException(/*like this*/new CountOutOfRangeException("Cannot count past 20")) ) );
You can filter the comment delimiters mentally (and Intellij Idea helps by coloring them a dull grey) and read the sentence, “mock expects a call at least once to method ‘updateCounter’ with a parameter equal to our expected increment and it will on consecutive calls return value 10, return value 20, and finally throw an exception like this. new CountOutOfRangeException().” I don’t know about you but I get a thrill when I write code that looks as good as I do. (…and I look pretty good for a geek.)
That’s not the point today. I didn’t drag you away from that porn site to preach how good JMock is. Instead I wanted to share a pattern I started noticing in my coding recently. Anytime I get the urge to return void from a method I instead declare the return to be the instance type. I then code “return this;” at the footer of the method and my APIs start to look quite pretty. I’ll give you a tour of an interface that I’m still back-patting myself over. (By the way, it’s never a good idea to feel good about some code you wrote as that’s normally a sign of bad code, but off back patting I go.)
The interface in the spotlight is called XMLDocumentBuilder. I use it to build XML strings from Tabels, Lists, maps, maps of lists, and sometimes the chewing gum from under the table. Here’s a unit test method demonstrating the use of the interface.
public void testGetGreatGrandParent()
XMLDocumentBuilder greatGrandChild = getXMLDocumentBuilderFactory()
XMLDocumentBuilder greatGrandparent = greatGrandChild.getGreatGrandParent();
You see here a factory that returns an instance of the XMLDocumentBuilder using a string which represents the root element. You then see a series of calls chained together to create the hierarchy that is the XML document. (You don’t see how I actually hard-coded the implementation in my unit test because I changed that code to a bogus getFactory() call right after I pasted here. I know, that’s cheating, right? Sue me.) Each call to createSubElement() returns the instance that was just created. There are methods defined on the interfacethat allow you to create and walk into a nested element as well as methods designed to let you walk back up the heirarchy. The unit test demonstrates the ability to walk up three levels in the hierarchy in one step. The interface was designed to replace a series of calls the a series of Concretes and interfaces on a specific 3rd party API. The idea was to generalize what we were trying to do while minimizing our number of dependancies. Where the old code had to deal with Node objects, Attribute objects, Documents and the like the revised code performed the same task using a single interface. The code was then generalized and no longer depended on the 3rd party API directly. I then implemented a wrapper around the 3rd party API and hid it behind the interface. In doing so I realized that the API was really not necessary as the majority of the implementation delegated to methods on the interface. (That’s just how powerful the interface was, it practically implemented itself.) There is a small amount of 3rd party code in the implementation which could easily be replaced by some simple custom coding.
So there you have it. A discussion that has nothing to do with XML has ended in a discussion about an XML building interface. I’m really not that big of an XML fanatic, really. I honestly hate XML because of how verbose it is and all the overhead involved in processing it. I’m just like the rest of you anti-XML zealots. C’mon, can I be down with y’all? I made posters and all. They say stuff like “XML is Bad! BAD BAD BAD!”, and “Give a whistle if you can’t stand DSSSL!”, and also “My CORBA service can kick your web service’s Ass!” I guess I should just get to the moral huh? The moral to the story is, the better you make your interfaces the more cool your fellow developers will think you are and then they’ll take you to the bar and get you drunk and then you can write a blog about it where somebody will stumble across your pathetic site and stuff a wad of Franklins in your pocket because they think you sound smart and smart people shouldn’t be without money so here take this $100000 (is that enough zeros?) because you’re so cool. There, that’s the moral. Aesop would be proud! Talk to me people…