Fluent Interfaces And Readability

posted on 05/29/08 at 10:52:45 pm by Joel Ross

I've heard people throwing the term "Fluent Interface" around, and, until recently, I had no idea what it meant - and more importantly, why I should care.

But then I started using one. At first, I didn't know I was using a fluent interface until I saw an example of a different one, and thought, "That's what I'm using!" I didn't even fully understand it at that point, but it definitely made the code a lot easier to follow.

So what was I using? Ninject, of course! For those who don't know, Ninject is a dependency injection framework, and it uses fluent interfaces for it's binding syntax. Here's an example of how you can bind an implementation to an interface to be used for a particular type:

Bind<IMessageLogger>().To<ConsoleLogger>().For<ConsoleService>();

In a typical implementation, the same thing is accomplished through a series of separate statements where you have to take into account the return types so you can deal with another call later on. Method names are typically longer because they're meant to be used in a stand alone fashion. By doing it this way, your method names tend to be shorter, but looking at that one line, it's pretty easy to see what's going on: I want to use ConsoleLogger whenever ConsoleService requests IMessageLogger.

So how do you create a fluent interface? It's pretty simple, really. I'll use orders and order lines as a simple example (even though Scott Hanselman is tired of Northwind!) I want to be able to write code like this:

order.AddLine("CPU", 2).AddLine("1GbRam", 4).SetTax("49456");

To accomplish that, we'd have an Order class that looks something like this:

   1:  public class Order
   2:  {
   3:     List<OrderLine> OrderLines { get; set; }
   4:   
   5:     public Order AddLine(string lineId, int quantity)
   6:     {
   7:        this.OrderLines.Add(new OrderLine(lineId, quantity));
   8:        return this;
   9:     }
  10:   
  11:     public Order SetTax(string zipCode)
  12:     {
  13:        // Calculate Tax based on Zip Code
  14:        return this;
  15:     }
  16:  }

The key is the return type. In this case, the two methods return Orders, so returning "this" is what we want - this allows you to chain your method calls together. You can also change the return type. This allows you (as the interface creator) to have control over what the next action can be.

Right now, I'm having fun using fluent interfaces. Eventually, I'll start to feel comfortable enough to start incorporating this into my own libraries. For now, I think I need to understand where it makes sense to use this a bit more first.

Tags: |

Categories: Development, C#

8 comments »


 

8 comments

Comment from: Alan Stevens [Visitor] Email · http://netcave.org
Nice post. Ninject is definitely one of the cleanest implementations of this that I've seen.

++Alan
05/29/08 @ 23:04
Comment from: Joel Ross [Member] Email · http://www.rosscode.com
Alan,

Thanks for the comment. I really, really like Ninject. I think part of it has to do with the easy access to it's creator on twitter, but still, it's so clean and easy to work with.

Joel
05/29/08 @ 23:11
Comment from: Michael Eaton [Visitor] Email · http://michaeleatonconsulting.com/blog
You know, as silly as it is, I always wondered how to accomplish that kind of thing.

Good post!
05/30/08 @ 22:54
Comment from: Mike [Visitor] Email · http://72miles.com
I thought, and still think, that fluent interfaces is more than just method chaining http://72miles.com/blog/posts/configuration-method-chaining/. It is more about creating code that is more readable, almost to the point where they are sentences.

Such as order.AddLine("CPU", 2).and.AddLine("1GbRam", 4).then.SetTax("49456").but.setDiscount(.1);

There is a subtle difference, but it adds a few more words to the line to make it more readable.

I may be wrong, but that's what I thought it was all about.
05/31/08 @ 14:22
Comment from: Dan Hounshell [Visitor] Email · http://danhounshell.com
Joel, thanks for sharing sharing the definition and examples. Very cool and very elegant.
06/02/08 @ 18:04
Comment from: Chris Roland [Visitor] Email · http://www.christohperroland.com/blog
Great post. These types of posts rock.
06/05/08 @ 23:45
Comment from: Joel Ross [Member] Email · http://www.rosscode.com
Dan & Chris,

Thanks for the feedback. I appreciate the support. A lot of times I don't understand the terms I hear, so once I figure them out, the easiest way to solidify that learning for me is to write it out - teach others, in a way.
06/05/08 @ 23:56
Comment from: Joel Ross [Member] Email · http://www.rosscode.com
Mike,

Thanks for the comments. I do see some value in what you're saying. By controlling the return types through methods like "then" you not only get better readability, you also can control return types so after then, your only options could be setting tax and saving, for example.

I think the original is just as readable withoutt the "and" methods. The problem I see with methods like "and" is that they really do nothing but "return this;", which to me is a waste. Maybe I'm missing something, but it doesn't seem worth the extra effort.
06/06/08 @ 00:11

Leave a comment


Your email address will not be revealed on this site.

Your URL will be displayed.
(Line breaks become <br />)
(Name, email & website)
(Allow users to contact you through a message form (your email will not be revealed.)