Fluent interfaces

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

Post Reply
alex.barylski
DevNet Evangelist
Posts: 6267
Joined: Tue Dec 21, 2004 5:00 pm
Location: Winnipeg

Fluent interfaces

Post by alex.barylski »

What are your feelings towards them?

Do you find they have actually cleaned up your code or made it worse?

Please be as detailed as possible in your arguments...giving concrete examples when appropriate...
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

Depends on what you use it for. I've seen two extreme zones of possible usage - one approach where everything must be a fluent interface just for the sake of it, and another where fluent interfaces are only used when they track some pattern on the code.

The first should be avoided at all costs since the only thing it serves to do is create mounting confusion - far better to stick to listing method calls. The approach typically involves linking unrelated method calls. Consider:

Code: Select all

$foo = new Foo;
$foo->addItem(new Item)->setPrice(65)->formatOutput();
echo $foo;
In a single line you've
- added an Item
- set a Price
- formatted Output

These aren't really related. They were thrown together because it looks neat. Functionally it certainly works, but is it a good idea to attach formatting and pricing together?

The other extreme relies on being fluent to a pattern. A simple case is when you need access to the method of an internal object. For example say we have class Foo, which has an instance of class Bar. Bar has a method you need to access from the public API so you could do something like:

Code: Select all

$foo = new Foo;
$foo->getBar()->setBarProperty(123);
The pattern here is accessing sub-objects from a parent object's API. Personally I avoid it in the public API of libraries, but use it internally since it can be pretty useful.

Another pattern is generating an API which resembles English. This has debatable value depending on how you implement it (primarily because the API is deliberately generic and does not hint at the classes each API method is attached to) but I've found it useful in cases where the API is frequently used to describe something. A simple example using PHPMock might look like:

Code: Select all

require_once 'PHPMock.php';

interface Foo {
    public function foo($bar);
}

$fooMock = PHPMock::mock('Foo');
$fooMock->shouldReceive('foo')->withAnyArgs()->times(5)->andReturn(true);

for($i=1;$i<5;$i++) {
    $fooMock->foo();
}

$fooMock->verify();
At the end of the day, fluent interfaces are useful if they are properly thought out. If you can't think of a good reason for using one in your current code, then you probably don't need to.
User avatar
Kieran Huggins
DevNet Master
Posts: 3635
Joined: Wed Dec 06, 2006 4:14 pm
Location: Toronto, Canada
Contact:

Post by Kieran Huggins »

Fluid interfaces make me smile. I love not having to store every damn thing in variables and pass them around.
User avatar
superdezign
DevNet Master
Posts: 4135
Joined: Sat Jan 20, 2007 11:06 pm

Post by superdezign »

I've never heard the term before.
Ever since I looked at the Zend Framework, I started returning $this at the end of methods with no return value, so I tend to link the methods together similarly.. Is that the same thing being describe here?

i.e.

Code: Select all

$module = new Vol_Module();
$module
    ->addData('Foo', $someArray)
    ->addData('InnerFoo', $compositeView)
    ->mergeModule($anotherModule)
    ->build();
I only do it to simulate the with($object) functionality of other languages, but without the ambiguity and bugginess.
User avatar
Kieran Huggins
DevNet Master
Posts: 3635
Joined: Wed Dec 06, 2006 4:14 pm
Location: Toronto, Canada
Contact:

Post by Kieran Huggins »

I've usually heard it called "method chaining"
User avatar
superdezign
DevNet Master
Posts: 4135
Joined: Sat Jan 20, 2007 11:06 pm

Post by superdezign »

Ahh.. I see.
Wikipedia and I are getting well-acquainted today.
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post by Jenk »

In Smalltalk, the default return value is the equivalent of $this ('self'.) i.e. if you don't specify a return value, it will return $this.

However, due to the nature of Smalltalk it is not advised to method chain.

Although Smalltalk supports cascading:

Code: Select all

someObject
  doSomething;
  doSomethingElse;
  doSomethingAgain;
  doSomethingOrOther
is the same as:

Code: Select all

someObject doSomething.
someObject doSomethingElse.
someObject doSomethingAgain.
someObject doSomethingOrOther
VB6 also has similar, using the with statement:

Code: Select all

With SomeObject do
  .doSomething
  .doSomethingElse
  .doSomethingAgain
  .doSomethingOrOther
End With
I wouldn't mind seeing similar in PHP, preferably closer to the Smalltalk implementation of just using a simple character to relay the method, rather than a whole construct/block/function like that of VB's.
alex.barylski
DevNet Evangelist
Posts: 6267
Joined: Tue Dec 21, 2004 5:00 pm
Location: Winnipeg

Post by alex.barylski »

Maugrim_The_Reaper wrote:These aren't really related. They were thrown together because it looks neat. Functionally it certainly works, but is it a good idea to attach formatting and pricing together?
Thats basically what I've been asking myself...while I agree they are not related but it looks neat...

It does (when formatted correctly) allow you to group multiple method calls under a single object reference:

Code: Select all

$obj->doSomething()
       ->doSomethingAgain()
       ->oneMoreTime();
I see this as a similar technique to VB and it's 'with' statement which saves on typing the $object identifier over and over again. On one hand, although I don't find the code much clearer, it is clearer in the sense there is less to look at. You also avoid possible typo-errors of typing $ojb and $obj.

On the flipside, it's now impossible to group related statements inside a conditional, whereas it's a trivial matter if they are their own individual statements...
A simple example using PHPMock might look like:
That is probably the best example of usage. Ok, you have essentially nailed the three uses that I seen for fluent interfaces. I have up til now, used fluent interfaces for accessing parent or composite objects, as opposed to accessing the members directly - I set all member variables to private so once I switched to PHP5 it was a given.

For for grouping the method calls of an object is warranted or not...I think if you look at from the perspective that the methods are not related and should be separated into individual statements, then it makes sense to keep them separate (also like I said, it's impossible to execute those statements conditionally now). However, from a slightly higher perspective 'with proper formatting' I can also see it as being a benefit. I guess the latter is purely personal taste as it's more to do with reducing typing and increasing clarity strictly through formatting.
Fluid interfaces make me smile. I love not having to store every damn thing in variables and pass them around.
That is sort of the direction I'm heading in...
VB6 also has similar, using the with statement:
That is basically what it reminded me off when I looked at it from an usage of something other than just method chaining...and I thought...cool...I remember using that in VB4 :P
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

We should be clear that technically what people are talking about 99% of the time is setters that return $this. Setters are one of the few types of methods that do not have a return value. And being able to put all setter calls on one line can be a convenience.

However, this like many such hot features are mainly loved by cargo-cultists.
(#10850)
Post Reply