Sunday, January 6, 2013

Method chaining

Jsoup is a great Java library for parsing HTML pages. Its API is elegant and easy to use. One feature I love is the way it allows a webpage to be parsed from a website URL (as opposed to providing the HTML page from a String or Reader object). It uses a technique called method chaining to build and send the HTTP request that will retrieve the webpage.

You start out by passing the URL into the Jsoup.connect(String) method. This method returns a Connection object, but you're not supposed to assign this object to a variable, as is done typically. That's not how method chaining works. Instead, you continue calling methods one after another without assigning their return values to anything. You can call as many or as few of these methods as you want. They allow you to customize the HTTP request. They all return a reference to the same Connection object (i.e. return this;), which is what allows you to call the methods in a chain-like fashion. The methods allow you to specify things like cookies and the connection timeout.

In addition to using method chaining, Jsoup.connect(String) also uses a sort of factory pattern, since its purpose is to construct an HTTP request, send it, and then parse the returned HTML page into a DOM. So, there has to be a method that terminates the chain and returns the object we want it to build. In Jsoup's case, the termination method elegantly serves an additional purpose: specifying the HTTP method (which all HTTP requests must have).

The example below parses the HTML page of google.com. It assigns some cookies to the request, specifies a connection timeout of 60 seconds, and then sends the request using the GET method.

Map<String, String> cookies = ...
Document doc = Jsoup.connect("http://www.google.com")
                    .cookies(cookies)
                    .timeout(60000)
                    .get();

Inspired by Jsoup, I've done something similar with my ez-vcard project (I will be releasing these changes in the next version of the library). To parse a vCard, you no longer have to use the relatively cumbersome VCardReader class. You can now use a method chaining API, which calls VCardReader behind the scenes. It reduces the amount of boilerplate code, making the code easier to read and understand.

//using VCardReader
File vCardFile = ...
Reader reader = new FileReader(vCardFile);
VCardReader vcr = new VCardReader(reader);
VCard vcard = vcr.readNext();
reader.close();

//using method chaining
File vCardFile = ...
VCard vcard = Ezvcard.parse(vCardFile).first();

Similarly, method chaining can be used to write a vCard as well.

//using VCardWriter
VCard vcard = ...
File vCardFile = ...
Writer writer = new FileWriter(vCardFile);
VCardWriter vcw = new VCardWriter(writer, VCardVersion.V3_0);
vcw.write(vcard);
writer.close();

//using method chaining
VCard vcard = ...
File vCardFile = ...
Ezvcard.write(vcard).version(VCardVersion.V3_0).go(vCardFile);

Method chaining can make your code a lot easier to read and understand. Have you used method chaining before? Leave a comment below.

4 comments:

Gerard thomas said...

Very good read. This is so important. Know your Target Audience!!

Michael Angstadt said...

Thanks

Jason said...

I haven't, but I was recently "schooled" on the "Builder" pattern in Java which leverages method chaining and can make your code much more readable. I've been thinking I should probably replace some of my ugly massive constructors with a Builder!

Michael Angstadt said...

Hi Jason, I think that if you have a constructor with more than 5 parameters, you should try to replace it with a builder (or just rely on setters to populate the object). The builder pattern is also useful for constructing immutable objects.