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#