The concept of fluent interfaces is certainly not new to me, but recently I’ve had the chance to bring it into practice by using EasyMock 2. EasyMock 2 uses the features of Java 5 to greatly simplify the programming model for mock objects.
This got me thinking about XML, and DOM in particular. Let’s say we want to create the following piece of XML using DOM:
<contacts>
<contact>
<name>John Doe</name>
<phone type="home">555-12345</phone>
<phone type="work">555-67890</phone>
</contact>
</contacts>
Basically, the way to create this XML using the standard DOM API look something like:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.newDocument();
Element name = doc.createElement("name");
name.setTextContent("John Doe");
Element phone1 = doc.createElement("phone");
phone1.setAttribute("type", "home");
phone1.setTextContent("555-12345");
Element phone2 = doc.createElement("phone");
phone2.setAttribute("type", "work");
phone2.setTextContent("555-67890");
Element contact = doc.createElement("contact");
contact.appendChild(name);
contact.appendChild(phone1);
contact.appendChild(phone2);
Element contacts = doc.createElement("contacts");
contacts.appendChild(contact);
doc.appendChild(contacts);
Not really fluent in any way, and quite verbose as well.Things improve somewhat with JDOM, because it uses concrete classes rather than interfaces, so we don’t need the Document factory. Also, most methods conveniently return this, so you can chain them:
Document doc = new Document();
Element name = new Element("name").setText("John Doe");
Element phone1 = new Element("phone").setAttribute("type", "home").setText("555-12345");
Element phone2 = new Element("phone").setAttribute("type", "work").setText("555-67890");
Element contact = new Element("contact").addContent(name).addContent(phone1).addContent(phone2);
Element contacts = new Element("contacts").addContent(contact);
doc.addContent(contacts);
This is certainly less verbose, but is it fluent? Those two concepts don’t necessarily mean the same thing. An API can be very succinct, but that does not necessarily mean it is fluent. Quoting Martin Fowler: The [fluent] API is primarily designed to be readable and to flow. The example above doesn’t flow; the approach is quite linear, even though we are creating a hierarchal tree.
Compare this to the API of XLinq, the XML component of Microsoft’s LINQ effort1:
XElement contacts =
new XElement("contacts",
new XElement("contact",
new XElement("name", "John Doe"),
new XElement("phone", "555-12345",
new XAttribute("type", "home")),
new XElement("phone", "555-67890",
new XAttribute("type", "work"))
)
);
Note that by indenting (and squinting a bit) the code to construct the XML tree shows the structure of the underlying XML. And this is due to the way the API was designed: clever use of .NET operator overloading, variable constructor arguments, and generics allow for this.
I really miss something like this in the Java space. So much, in fact, that I’m thinking about writing one myself. We don’t have operator overloading in Java, but we do have static imports. So that could mean we could end up with something along the lines of:
import static org.easydom.EasyDom.*;
Element contacts =
element(name("contacts"),
element(name("contact"),
element(name("name"), "John Doe"),
element(name("phone"), "555-1234",
attribute(name("type"), "home")),
element(name("phone"), "555-567890",
attribute(name("type"), "work"))
)
);
The idea is that the EasyDom object contains nothing but static methods, just like EasyMock does. The name() method, for instance, returns a QName, necessary to construct an element using element(), or an attribute using attribute(). I’m not sure about the name() requirement, dropping that would make it more consice. So far, so good.
There are two things stopping me from creating this EasyDom API:
-
Does the world really need another DOM API? I thought about returning or wrapping W3C DOM, but that would mean that only the DOM creation API is fluent, and the end result still has weird behavior like using
NodeList where a List<Element> would be nicer.
-
Creating a DOM API is hard. Creating a fluent interface is even harder. I’ve got enough work already.
Leave a comment to change my mind :).
Update 20070514: Guillaume is right when he says that the Groovy Markup builder has a fluent interface. And on that note, Tareq Abed Rabbo has written a nice post about combining Groovy with Spring-WS.
<hr/>
1 - If you don’t know anything about LINQ, read this. I wish we had something similar in the Java space.