The danger of Fluent interfaces

December 30th, 2005 by Ivo

After Martin, Mike and Paul have demonstrated the usefulness of Fluent Interfaces, I'd like to take a look at the downside.

Ironically, Martin's original example already demonstrates the problem:

 
customer.newOrder()
                .with(6, "TAL")
                .with(5, "HPK").skippable()
                .with(3, "LGV")
                .priorityRush();
 

newOrder could be implemented in two ways:
a) Create an order and return that new order
b) Add an order to this customer and return the customer

b is the fluid approach, a is more logical. In this example it's a.

This can lead to confusion; the 'with()' method employs method b. If you think that newOrder returns an Order, and hence 'with()' is a method of the Order class, look at 'skippable'. Martin states that order lines are skippable. So what's in front of skippable() is an OrderItem. That means that with() must have returned an OrderItem. If that is true, then with() is a method of OrderLine too!

So this means that in this case, probably both Order and OrderLine have a with() method. Apart from the fact that a method named 'with()' is dangerous as this can mean anything (which might be problematic for the non-fluent programmer), apparently in this case we have to add additional methods (both to Order and OrderLine) in order to be able to use this fluently.

priorityRush further demonstrates this. Where the second with() returned an OrderLine, the third with() now returns the order as priorityRush() is a method of Order.

While this *looks* more readable, it is actually confusing. Or perhaps priorityRush is also a method of OrderLine, that forwards the call to its order. That would only further clutter things up as now OrderLine and Order seem to have a similar API. OrderLine has a priorityRush() method that doesn't belong there, or Order has a skippable() method that doesn't belong there .

It becomes more clear if you write out the API that is needed to create this example:

 
  class Customer
     public Order newOrder()
 
  class Order
    public OrderLine with(int quantity, String code)
    public Order priorityRush()
 
  class OrderLine
    public OrderLine with(int quantity, String code)
    public OrderLine skippable()
    public Order priorityRush()
 

To create such an API only to be able to write more english-like sentences in code is, in my opinion, bad practice.

Only use this when it makes sense. If you like, make an add() method return the added item, so you can write stuff like:

 
  item = table.add(new Item());
  item.setBorder("red");
 

But not for readability purposes.

4 Responses to “The danger of Fluent interfaces”

  1. December 30, 2005 at 11:18 am, Patrick said:

    I agree. It makes both code and API more confusing than readable to me.

    This reminds me of something I heard a while ago and stuck with me ever since: “think vertical, not horizontal”, i.e. don’t be afraid to use more lines instead of glueing it all together. Especially if you’re glueing together different materials…

  2. December 30, 2005 at 4:45 pm, Mike's sudden inspirations said:

    I’ve read some posts about "fluent interfaces" and I have to agree that it’s a bad idea to sacrifice a reasonable API to be able to write english code.

    It’s anyway possible to accomplish that to some extent on a need-by-need basis with a class

  3. December 31, 2005 at 4:57 pm, Hannes og vefurinn said:

    Fluent interfaces vir

  4. January 06, 2006 at 5:02 pm, Stuardo -StR- Rodr said:

    I think you are right with the skippable example, because it is talking about the HPK, but the newOrder is understandable, because it is supposed to return an order (.newOrder is like $order = new Order… but attached to the customer).. I still think it is very readable