Tuesday, August 21, 2007

Here's a quick example of how to build a simple "Fluent Interface" in C#:

What if you've never heard of a Fluent Interface? Then I'd recommend reading this overview article.

Let's propose we have 2 classes for an e-commerce website: Order and OrderItem.

Let's also propose we want to mark the order as having free shipping, include an item in the order (with an ItemID of 15), and make sure the purchaser is not charged tax for this particular item (15).

This might typically be done with the following theoretical code:
order.FreeShipping = true;
OrderItem item = new OrderItem(15);
item.SuppressTax = true;
order.AddItem(item);

However, the intention of a Fluent Interface is to accomplish the same thing, but with a more "fluent" syntax, such as:

order
    .AddFreeShipping()
    .IncludeItem(15)
        .SuppressTax();

So, how is this accomplished in code?  Well, here is the code for both the Order and the OrderItem classes:
Order.cs
using System;
using System.Collections.Generic;

namespace TestFluentInterface
{

    public class Order
    {

        private List _orderItems = new List();
        private bool _freeShipping = false;
        
        public List OrderItems
        {
            get
            {
                return _orderItems;
            }
        }

        public bool FreeShipping
        {
            get
            {
                return _freeShipping;
            }
        }

        public OrderItem IncludeItem(int itemID)
        {
            OrderItem item = new OrderItem(itemID);
            _orderItems.Add(item);
            return item;
        }

        public Order AddFreeShipping()
        {
            _freeShipping = true;
            return this;
        }

    }
}

OrderItem.cs
using System;

namespace TestFluentInterface
{
    public class OrderItem
    {

        private bool _hasTax = false;
        private int _itemID;


        public OrderItem(int itemID)
        {
            _itemID = itemID;
        }

        public int ItemID
        {
            get
            {
                return _itemID;
            }
        }

        public bool HasTax
        {
            get
            {
                return _hasTax;
            }
        }

        public OrderItem SuppressTax()
        {
            _hasTax = false;
            return this;
        }

    }
}

And how do we prove that the code behaved as we intended?

By using a unit test, of course:
NUnit Test
using System;
using NUnit.Framework;
using TestFluentInterface;

namespace Test
{
    [TestFixture]
    public class Test
    {

        [Test]
        public void MainTest()
        {

            Order order = new Order();

            order
                .AddFreeShipping()
                .IncludeItem(15)
                .SuppressTax();

            Assert.That(order.OrderItems.Count == 1);
            Assert.That(order.OrderItems[0].HasTax == false);
            Assert.That(order.FreeShipping == true);

        }

    }
}

I hope this article has helped to give you a feel for an example code structure behind a fluent interface.

If you found this post helpful, please "Kick" it so others can find it too:

kick it on DotNetKicks.com
8/22/2007 10:56:15 AM (Central Daylight Time, UTC-05:00)
I would argue that this type of coding is not "fluent." It's more like a mumble interface.

do.a.bunch.of().stuff.on().one.line.and.pray(15).that.the.next.developer.can.read.this == true.

tom
8/22/2007 1:44:45 PM (Central Daylight Time, UTC-05:00)
Just noted AddFreeShipping isn't a good name, since I thought at first its functionality was to add a new object to some internal collection at Order. Looking inside, saw it set a property. I would change it to SetFreeShipping or something similar to instead.

--
Luciano Evaristo Guerche
Taboão da Serra, SP, Brazil
8/22/2007 3:12:44 PM (Central Daylight Time, UTC-05:00)
tom,

I am not advocating that fluent interfaces are appropriate in general for E-Commerce solutions, etc. I was just seeing a lack of articles explaining what the underlying code looks like for a fluent interface and thought I'd have fun coding one myself.

Besides, if the continuous.one.line() is the issue, you can put the methods on different lines:

order
    .AddFreeShipping()
    .IncludeItem(15)
        .SuppressTax()

In fact, I like that and I think I will change the code listing in the article.
Troy DeMonbreun
8/22/2007 3:13:37 PM (Central Daylight Time, UTC-05:00)
Luciano,

Yes, I struggled with the "Add" prefix myself (I even first considered using "Set" as well), but for the sake of time (my blog time is limited), I chose "Add". I also considered FlagAsFreeShipping(). No doubt many others can come up with a better naming convention.
Troy DeMonbreun
8/28/2007 12:22:33 PM (Central Daylight Time, UTC-05:00)
Unfortunately this suffers from the same limitation as Java:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6373386

So this works in simple cases but not if you want to extend your order/item classes.

Not to mention that you now need to create properties _and_ methods for every writable property... What a pain.
Different Tom
8/28/2007 2:18:55 PM (Central Daylight Time, UTC-05:00)
Hi Troy,

There are detractors, but I actually like this style of programming. It reminds me of the oldschool Smalltalk crowd. Just a small comment:

The idea of putting the method calls on different lines is interesting, but the way you've done it in the article implies that all of the methods are actions upon the "Order" instance, when in fact the suppressTax() call is made to an "OrderItem". You might want to further indent the suppressTax() call for clarity (though I guess clarity is subjective, your mileage may vary).


jim d
8/29/2007 10:36:20 AM (Central Daylight Time, UTC-05:00)
Different Tom,

Thanks, that's a very insightful angle.

Well, on the bright side, it only applies if your subclass adds or overrides a fluent method that returns "this".

Maybe the C# 4.0 language spec will incorporate a "Self-Type" syntax as exists in SmallTalk.
Troy DeMonbreun
8/29/2007 10:41:04 AM (Central Daylight Time, UTC-05:00)
jim d,

I totally agree that placing the .SuppressTax() in-line with the rest is misleading. I overlooked that when I edited the syntax. Thanks for the catch.
Troy DeMonbreun
9/12/2007 3:22:42 PM (Central Daylight Time, UTC-05:00)
Good to see an example using a domain model, I've tried it a few times for fun before but never felt at all happy with the result and I'm equally not that impressed with the idea of having a seperate set of fluent classes (Expression Builder approach).

I guess the result can be good but the effort involved in supporting two interfaces seems to be to be quite high.
9/13/2007 11:45:39 AM (Central Daylight Time, UTC-05:00)
You can also use extension methods to create a Fluent Interface.
9/13/2007 1:31:45 PM (Central Daylight Time, UTC-05:00)
Mohammad,

Could you elaborate?

If I understand Extension Methods correctly, as ScottGu describes them: "Extension methods allow developers to add new methods to the public contract of an existing CLR type, without having to sub-class it or recompile the original type.", then I'm not sure why extension methods would be noteable regarding Fluent Interfaces since these FI methods are (at least in my example) typically "setters".
Troy DeMonbreun
Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):