Fluent interfaces
Moderator: General Moderators
-
alex.barylski
- DevNet Evangelist
- Posts: 6267
- Joined: Tue Dec 21, 2004 5:00 pm
- Location: Winnipeg
Fluent interfaces
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...
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...
- Maugrim_The_Reaper
- DevNet Master
- Posts: 2704
- Joined: Tue Nov 02, 2004 5:43 am
- Location: Ireland
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:
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:
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:
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.
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;- 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);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();- Kieran Huggins
- DevNet Master
- Posts: 3635
- Joined: Wed Dec 06, 2006 4:14 pm
- Location: Toronto, Canada
- Contact:
- superdezign
- DevNet Master
- Posts: 4135
- Joined: Sat Jan 20, 2007 11:06 pm
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.
I only do it to simulate the with($object) functionality of other languages, but without the ambiguity and bugginess.
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();- Kieran Huggins
- DevNet Master
- Posts: 3635
- Joined: Wed Dec 06, 2006 4:14 pm
- Location: Toronto, Canada
- Contact:
- superdezign
- DevNet Master
- Posts: 4135
- Joined: Sat Jan 20, 2007 11:06 pm
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:is the same as:VB6 also has similar, using the with statement:
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.
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;
doSomethingOrOtherCode: Select all
someObject doSomething.
someObject doSomethingElse.
someObject doSomethingAgain.
someObject doSomethingOrOtherCode: Select all
With SomeObject do
.doSomething
.doSomethingElse
.doSomethingAgain
.doSomethingOrOther
End With-
alex.barylski
- DevNet Evangelist
- Posts: 6267
- Joined: Tue Dec 21, 2004 5:00 pm
- Location: Winnipeg
Thats basically what I've been asking myself...while I agree they are not related but it looks neat...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?
It does (when formatted correctly) allow you to group multiple method calls under a single object reference:
Code: Select all
$obj->doSomething()
->doSomethingAgain()
->oneMoreTime();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...
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.A simple example using PHPMock might look like:
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.
That is sort of the direction I'm heading in...Fluid interfaces make me smile. I love not having to store every damn thing in variables and pass them around.
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 VB4VB6 also has similar, using the with statement:
- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
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.
However, this like many such hot features are mainly loved by cargo-cultists.
(#10850)