Fluent Interfaces Require Fluent Situations

By | December 29, 2005

My friend and coworker Mike Naberezny wrote recently of fluent interfaces (a.k.a. object chaining). You should read more about it there, but in the mean time, here’s an example usage provided by Mike for a new customer order with multiple lines and a shipping priority:

$customer->newOrder()
         ->with(6, ‘TAL’)
         ->with(5, ‘HPK’)->skippable()
         ->with(3, ‘LGV’)
         ->priorityRush();

The idea is that the fluent interface makes it very easy to read the resulting code in a way that flows naturally. This is cool, but I want to add a caveat to his examples.

I think, for a fluent interface to be effective, you need situations where you actually have all that information at one time so that you can chain the methods in a fluid way. In the above case, we actually know what the whole order is, what the shipping priority is, etc. But very often (I would say “almost always”) you *don’t* know what the whole customer order is. Instead, you are more likely to iterate through the order lines as the customer adds them, or as you retrieve them from some data source. You might need something more like Mike’s initial contra-example:

$order = $customer->newOrder();
foreach ($line as $item) {
    $order->with($item['number'], $item['type']);
}

That’s not to say that a fluent interface cannot perform in such a way (it can if written to allow for it). My point is that writing class methods in a fluent interface style may not be worth the effort when you don’t often expect to have all the necessary information at once; however, if you *do* expect to have that information, it can be very nice. For example, I could see where a fluent interface could be quite effective in something like Solar_Sql_Select. Currently, you need to do this:

$select = Solar::object('Solar_Sql_Select');
$select->cols('*');
$select->from('table_name');
$select->where('colname = ?', $value);
$select->order('colname DESC');
$result = $select->fetch('all');

But with a fluent interface, one could do it this way (which seems very clean to me at this point):

$select = Solar::object('Solar_Sql_Select');
$select->cols('*')
       ->from('table_name')
       ->where('colname = ?', $value)
       ->order('colname DESC');
$result = $select->fetch('all');

Anybody have thoughts on this particular style of expression?

20 thoughts on “Fluent Interfaces Require Fluent Situations

  1. Pingback: The Infotainment Telesector

  2. Piers Cawley

    Martin Fowler noted that the order example was a quick idea; I think it suffers from slightly dodgy method naming; ‘with’ makes sense for the kind of static order creation example he gives, but maybe a better name would be ‘add’ or ‘addItem’.

    Reply
  3. Travis Swicegood

    To me, the first example is unreadable. Of course, I’m sure if I’m looking at the code, I would know what a TAL and HPK are, but from an example point, your example works better – guess I should take that up with Folwer though :-)

    As far as using it as the actual design of the code, I think it look good, but this is something that could definitely be abused. I do think the JMock examples and your example show two interfaces that could work. I really don’t like the idea of each of those methods return $this just to allow chaining though. Might just a paradigm thing…

    Reply
  4. Jason Sweat

    I wrote about method chaining earlier:
    http://blog.casey-sweat.us/?p=33

    I have a somewhat real world example of using a fluent interface (from a file last modified a year ago, before I ever heard of the term, but after having read Domain Driven Design) which uses a modified version of the specification pattern to chain together a series of specifications into a policy. This method comes from a policy factory object from which you can request a particular policy. Most of the $this->something methods you see below are internal factory methods to generate concrete specification objects.

    private function issuePolicy() {
    return $this->validLock()->and_(
    $this->fieldEquals(
    ‘PNDG_INDIC’,’Y’
    ,’This ATMC is no longer pending (has already been issued).’) ->and_(
    $this->requiredField(‘CUST_IDFTN’)->and_(
    $this->requiredField(‘BEGIN_DATE’)->and_(
    $this->requiredField(‘END_DATE’)->and_(
    $this->fieldGtFieldDate(‘END_DATE’, ‘BEGIN_DATE’, ‘End Date must be greater than Begin Date’)->and_(
    //MIN_WGT not a required field (allowed to be 0)
    $this->fieldGt(‘MIN_WGT’, -1, ‘Minimum volume must be 0 or more’)->and_(
    $this->fieldGt(‘MAX_WGT’, 0, ‘Maximum volume must be greater than 0′)->and_(
    $this->fieldGtField(‘MAX_WGT’, ‘MIN_WGT’, ‘Max must be greater than Min volume’)->and_(
    $this->requiredField(‘MAX_WGT’)->and_(
    $this->requiredField(‘MKT_GRP’)->and_(
    $this->requiredField(‘MKT_MGR’)->and_(
    $this->requiredField(‘CONT_SK’)->and_(
    $this->requiredField(‘TERMS_HASH’)->and_(
    $this->requiredField(‘METAL_CODE’)->and_(
    $this->requiredField(‘PRCNG_METHOD’)->and_(
    $this->fieldGt(‘HAS_COMP’, 0, ‘Must specify one or more Competitors.’)->and_(
    $this->fieldGt(‘HAS_ALLOY’, 0, ‘Must specify one or more Alloy/Tempers.’)
    )))))))))))))))));
    }

    Reply
  5. pmjones Post author

    Hi Jason –

    This is a perfect example of what I meant when I said you need “all the information at one time” for a fluent interface to really be useful. The policy factory is not the kind of thing you would do iteratively; you have in-hand the exact specification you want for the policy, and can use the chaining effectively. Thanks for the example, and the link back to your original.

    (BTW, got my copy of your patterns book a while ago; great stuff, man. :-)

    Reply
  6. pmjones Post author

    Hi Piers — I agree with you on the method naming; item() or something like that would make more sense.

    Reply
  7. Alan Knowles

    looks like a kludgy workaround for not having a “with ($obj) { }” construct..

    Although it looks neat from a layout point of view, from a programming point of view, It’s pretty horrific, returning $this as the return value, just to do it, make me shivver with fear… – You never know if a function returns something usefull or just $this.., and destroys the idea of methods returning something meaningfull..

    It’s far better to overload the methods to accept arrays in the first example, far clearer, and even shorter..

    Reply
  8. Mike Naberezny

    Hi Alan,

    I agree with you to a point, and I was also reminded of the Microsoft-ish “with” construct when I first started looking at them.

    I think that employing this technique requires both carefully considering the context where it will be used and then strictly adhering to the convention. Either every setter returns $this, or none of them do. Even one exception makes it too confusing, so it’s all or nothing.

    It’s a matter of taste, but I usually prefix setters with “set”. If you stick to this convention then it’s easy to remember which ones return $this.

    I suppose it is probably worth noting that from the engine’s point of view, fleunt interfaces are faster than building arrays and passing them. However, the difference is not really signficant.

    From my perspective, I think that the applications for fluent interfaces are limited, although some examples work out very nicely. I think that Paul’s SQL builder example is a good one.

    Mike

    Reply
  9. Ivo Jansch

    Why not take it the full monty then, and write:

    $select = Solar::object(‘Solar_Sql_Select’);
    $result =
    $select->cols(‘*’)
    ->from(‘table_name’)
    ->where(‘colname = ?’, $value)
    ->order(‘colname DESC’)
    ->fetch(‘all’);

    Reply
  10. Pingback: Full(o)bloG » Blog Archive » PHP e programmazione fluente

  11. pmjones Post author

    Hi Ivo — regarging the “full monty”, there’s no reason not to. I wanted to point out that one can write an interface that accepts both fluent usage and iterative usage, but looking at it now I didn’t make that clear at all. Thanks for asking about it. :-)

    Reply
  12. Jean-Eric

    I do appreciate your teaching-oriented-devlopment-skills.

    By the way, aren’t fluent interfaces another way to try to reach the never reachable MVC paradigm ? (i.e: Solar architecture)

    Reply
  13. pmjones Post author

    Hi Jean-Eric — you’re very kind, thanks.

    I don’t think a fluent interface is necessarily related to model-view-controller pattern, though. I would call it more of an API style, rather than a “pattern” for development.

    Hope this helps clarify. :-)

    Reply
  14. Reno

    This can be useful for initializing an objet with multiple criteria. php doesn’t support named parameters so “fluent interface” is an alternative.

    class Select {
    //function __construct($cols, $from = ‘table’, $where = null, $order = null)
    //..
    }
    $select = new Select;
    // FROM and WHERE are skipped.
    // Not possible with the constructor version.
    $select->cols(‘*’)->order(‘colname DESC’)->fetch();

    Reply
  15. Pingback: StR » Blog Archive » Interfaces fluidas

  16. Pingback: Yuppii!! » Blog Archive » Fluent Interfaces in PHP

  17. Pingback: jansch.nl » Blog Archive » The danger of Fluent interfaces

  18. Pingback: Scott Hanselman's Computer Zen - The Weekly Source Code 14 - Fluent Interface Edition

  19. Pingback: La Maphpia » Blog Archive » Interfaces fluidas

  20. Pingback: Fluent Interfaces « Form Follows Function

Leave a Reply

Your email address will not be published. Required fields are marked *