Metaphysical Developer

Fluent Code

Posted in Languages by Daniel Ribeiro on April 12, 2009

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("me@gmail.com")
    .subject("metaphysics")
    .to("everybody@gmail.com")
    .cc("CIA@usa.gov")

Instead of the usual, full of noise:

Email email = new Email();
email.from("me@gmail.com");
email.subject("metaphysics");
email.to("everybody@gmail.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('me@gmail.com').
  subject('metaphysics').
  to('everybody@gmail.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: 'me@gmail.com';
        subject: 'metaphysics';
        to: 'everybody@gmail.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.
Advertisements
Tagged with: , , , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: