The beauty of fluent interfaces

May 15, 2008 at 11:43 PMAndre Loker

I notice that more and more frameworks, toolkits and libraries use fluent interfaces. Here are two examples:

Rhino Mocks

   1: [Test]
   2: public void SomeTest() {
   3:     var mocks = new MockRepository();
   4:     var service = mocks.CreateMock<IAccountService>();
   5:     using (mocks.Record()) {
   6:         Expect
   7:             .Call(service.Login(null, null))
   8:             .Constraints(Is.NotNull() & Is.NotSame("foo"), Is.NotNull())
   9:             .Repeat.AtLeastOnce()
  10:             .Return(LoginResult.Failed);
  11:         // ...
  12:     }
  13: }

Castle Windsor

   1: var container = new WindsorContainer();
   2: container.Register(
   3:     Component
   4:         .For<IAppContext>()
   5:         .ImplementedBy<AppContext>()
   6:         .Parameters(Parameter.ForKey("synchronized").Eq("true")));

[Maybe its not surprising that both libraries use fluent interfaces. Ayende Rahien, creator of Rhino Mocks, is also a huge contributor the the Castle project. ]

Most of the time I really like fluent interfaces. Not only does the code reveal its intention more clearly - at least most of the time. You also get support while writing the code: depending on the FI, the object returned by a call to an FI method might only a reduced set of methods that are meaningful at that point which drives you more into the correct direction.

If you write a FI for a library yourself, be sure to not overdo it. Keep it simple and don't try to get everything FI-esque just because you know how to do it. One possible pitfall of fluent interfaces is that it is not always clear how many times a method should be called. For example, imagine the case where you have a Client class that can have multiple order Objects, each containing multiple OrderLine objects. This resembles the example of Martin Fowler. You decide to provide an FI for the operation of adding configuring an order, like so:

   1: var c = new Client();
   2: c.AddOrder()
   3:     .WithLine(10, "Apple", 0.49m)
   4:     .WithLine(4, "Banana", 0.59m)
   5:     .ExpressDelivery();

This looks certainly more fluent than the "conventional" way:

   1: var client = new Client();
   2: var order = new Order();
   3: order.ExpressDelivery = true;
   4: var line = new OrderLine();
   5: line.Quantity = 10;
   6: line.UnitCost = 0.49m;
   7: line.Product = "Apple";
   8: order.AddLine(line);
   9:  
  10: line = new OrderLine();
  11: line.Quantity = 5;
  12: line.UnitCost = 0.59m;
  13: line.Product = "Banana";
  14: order.AddLine(line);
  15:  
  16: client.AddOrder(order);

However, in the FI it is not that obvious that ExpressDelivery() should be called only once. Maybe the user wants to deliver the order twice (ok, the example is bad). The non-fluent version does not have this potential confusion. No one would assign order.ExpressDelivery multiple times and expect it to generate multiple deliveries.

Still, I think this is a small price to pay compared with the improved readability gained by FI.

Of course, the non-FI example is a worst case scenario. For example, you could provide constructor arguments to OrderLine. With C# 3.0 and object/collection initializers the example can be approved even more:

   1: var client = new Client();
   2: var order = new Order {
   3:     new OrderLine {Quantity = 10, UnitCost = 0.49m, Product = "Apple"},
   4:     new OrderLine {Quantity = 5, UnitCost = 0.59m, Product = "Banana"}
   5: };
   6: order.ExpressDelivery = true;
   7: client.AddOrder(order);

I even find the object initializer more expressive than the WithLine FI. I'm really looking forward to all the future APIs that will leverage fluent interfaces and C# 3.0  syntax features.

Posted in: C# | Snippets

Tags: , ,

Comments (8) -

commenter
United Kingdom commenter says:

Fluent Interface (n) - an API in which method names are baffling in isolation, but when called in the 'magic' correct order, combine to form code which resembles almost comprehensible broken English with lots of punctuation thrown in.

I like FI and I was thinking about them earlier. My one concern with them is how it can cause difficulty with debugging. The way you have your code formatted each method call is on a separate line which at least allows for placing breakpoints easily. One way to get carried away would be to place it all on one line. I have seen a bit too much of that.

Andre Loker
Andre Loker says:

@Brennan

Unfortunately Visual Studio won't allow you to place a breakpoint on one of the method calls, even if they are on different lines. You can only step into each of the calls.

At least you can mark trivial methods in the API with a [DebuggerStepThrough]-attribute to have the debugger ignore those calls.

@anonymous poster
Yes, that's certainly true. Designing an understandable FI is not easy. As I said, don't go crazy with FI just for the sake of it. They should be used in very specific scenarios rather than in a general purpose API.

Yet another feature from Smalltalk'80 has been rediscovered by the C-based technologies... The next one is the ability to run several debuggers at once in the same IDE; I expect another 2 to 3 years for that Smile

Take a look at syntax of Objective-C.

Nice Article.  You can set break points on sub-expressions in a line (see http://www.eggheadcafe.com/articles/20050511.asp)

Fluent Interfaces can greatly aid in the discoverability of your API but you should be mindfull of the increased complexity and maintenance costs associated with them.

Thanks for giving these examples of fluent interfaces! Laughing

Blogengine rocks! We want to impement it also. Is it easy?

Pingbacks and trackbacks (2)+