Fluent style of coding is quite important on internal Domain Specific Languages (DSLs) on languages that support OO. One of the key aspects of a fluent interface is method chaining, which allows the developer to reduce noise (the amount of repeated text) on the code by invoking several methods on the same object, one after the other. Just as in the good old fashioned Don’t Repeat Yourself (DRY) spirit, less noise.
However, fluent code is not only appropriate for DSLs. It can greatly improve code readability and intention revealing even on simple tasks, such as building a complex object.
For instance, let’s take a simple example of configuring an email. You can do lots of things to it, such as configure the sender, the subject, the body, and so on. In Java, such code would become:
new Email() .from("firstname.lastname@example.org") .subject("metaphysics") .to("email@example.com") .cc("CIA@usa.gov")
Instead of the usual, full of noise:
Email email = new Email(); email.from("firstname.lastname@example.org"); email.subject("metaphysics"); email.to("email@example.com"); email.cc("CIA@usa.gov");
A problem arises when the class you want to use method chaining on classes that are not already fluent and you do not have write access. Maybe you are using it from a library or from a Framework you have to use. On languages with static typing such as Java, the only solution is to create yourself a static wrapper (more specifically, a proxy) around it, and just delegate the methods to the real implementation.
On languages with support for method lookup alteration and interception, such as Ruby’s missing_method and Smalltalk’s doesNotUnderstand:, you can make such wrapper a general one. Example of such in ruby:
class Email attr_accessor :to, :from, :subject, :cc end class Wrapper def initialize(wrapped) @wrapped = wrapped end def method_missing(m, *args, &block) setter = m.to_s + '=' if @wrapped.respond_to?(setter) @wrapped.send(setter, *args, &block) else raise NoMethodError end return self end end mail = Email.new Wrapper.new(mail). from('firstname.lastname@example.org'). subject('metaphysics'). to('email@example.com'). cc('CIA@usa.gov')
The beauty is: Wrapper is general. You can take any object of any class you like and, just like that, use a fluent interface around it. You can do this in Smalltalk as well, but Smalltalk is naturally fluent, because of the semicolon:
email <- Email new from: 'firstname.lastname@example.org'; subject: 'metaphysics'; to: 'email@example.com'; cc: 'CIA@usa.gov'; yourself.
Regarding Java language, even though you cannot make a fluent universal wrapper as you can do in ruby (dynamic proxies can’t change the interface, neither can Aspectj), there are some good perspectives ahead:
- Chained Invocations on methods that return void. Unlikely to make the cut into the jdk 7
- This Type: which just makes it clearer that a interface is fluent. Also, eases up the fact that subclasses will return its type on fluent methods defined on the parent.