Fluent Interfaces: What am I missing?
Hey, that’s a really cool trick, returning a context specific object with clever names on it. Sometimes it even makes sense when I read it. More often than not, though, it doesn’t.
The problem is the developer who wrote the fluent interface is fluent in the underlying things being abstracted and the developer consuming the API isn’t. This is often why I am using an API, after all.
I don’t want to pick on any particular API, because I know that people who have written these things sweat over them and craft them and bleed into them. I am not saying your work sucks.
That said: Can someone help me understand what is fluent about this? To me, it just doesn’t read as we expect fluency to read. I get it because I have worked with the API for awhile, but that’s not the point is it? Isn’t the point of a fluent API to encourage discoverability?
With.Mocks( mocks ).Expecting( delegate
{
// Record expectations
Expect.Call( dependency.SomeMethod() ).Return( null );
})
.Verify(delegate
{
// Replay and validate interaction
IComponent underTest = new ComponentImplementation( dependency );
result = underTest.TestMethod();
});
How about this one? It reads a little better, but getting to this final composition takes a LONG time of understanding what each object along the way is meant to be and do.
var twitter = FluentTwitter.CreateRequest()
.Configuration.CacheUntil(2.Seconds().FromNow())
.Statuses().OnPublicTimeline()
.AsJson();
I am not saying there is no place for this style, but I do think our tools are a bit rough for this technique to be effective. As I try to discover the appropriate functionality within the API to accomplish what I am after, my Intellisense if clogged with all of the standard object cruft like .ToString().
This doesn’t help me find the right way to use the API and frankly I find this style to be extremely noisy. Is it just me?
This is a pretty good example of bad method chaining. The whitespace is pretty bad as well.
The nested-closure style to me is much more readable, and more discoverable as well.
var request = TwitterFactory.CreateRequest(x =>
{
x.SetCacheDuration(2.Seconds());
x.AccessPublicTimeline();
x.FormatAsJson();
});
request.Send();
The interface passed into the CreateRequest closure is narrowly constrained to the required information to prepare the request. You might even limit the scope further by having different methods/interfaces for different types of requests. That way the developer doesn’t have to guess which things need configured.
Also, make it easy to configure defaults in a similar way with the same syntax so the user doesn’t have to repeat themselves.
As Chris already mentioned, the best thing for a fluent interface is to narrow down the different options so that you automaically follow the flow that is laid out by the developer of the API. Martin Fowler calls this a progressive interface:
http://www.martinfowler.com/dslwip/MethodChaining.html
This can be achieved for an expression builder using method chaining as well.
I tend to believe that fluent interfaces tend to be better when they are better contained in smaller instances. Take NUnit, for instance:
Assert.That(contidition, Is.EqualTo(5))
This is an example of a fluent interface done correctly. It reads like english, and is much better than:
Assert.AreEqual(5, condition);
Just my 2 cents.
@BrianGenisio
I would take it a step further and using something like this:
Assert.True(contidition.IsEqualTo(5))
I use these types of extensions in some of out internal framework code because:
if(result.IsEqualTo(expected))
looks better than
if(result == expected)
Fluent interfaces can certainly be abused and overused as we are seeing that happen now. The good news is fluent interfaces help us create systems based more on convention and readability which in the end will help with maintenance.
Fluent NHibernate and Windsor Registration are two that really come to mind. No more xml hell and I can dot through the configuration with intellisense and the code becomes intention revealing. Very nice!
As for the two examples above they are abusive, but thats still a good thing. We need to test the limits before we know what works and what doesn’t.
I’ve blogged about this in the past and got walloped over the head for it but I’ll try one more time here: many of these interfaces are NOT fluent, they are LEGIBLE.
FLUENT implies that its easy to SAY something which (when intellisense is your only composition tool) is rarely the case with many of these. I don’t want to see verbs in my intellisense list, I want to see methods named something like the action I’m trying to achieve in my code. When I see .Is().Not()….. code, a little bit of the professional inside of me dies 🙂
It may very well be that once written these interfaces are indeed more LEGIBLE than otherwise, but calling them fluent as though they make it easier to SAY what you mean is a red-herring for all but the best of these approaches. Things like FluentNHibernate and the Windsor Registration syntax are arguably much better than their XML alternatives in many regards, but even if they had been coded in non-fluent-interface-style this same would be true of them IMHO.
“FluentNHibernate and the Windsor Registration syntax are arguably much better than their XML alternatives in many regards, but even if they had been coded in non-fluent-interface-style this same would be true of them IMHO”
I agree, I also find discoverability to be a missing part of the puzzle. I haven’t explored this space massively but I like fluent interfaces to be very discoverable, I want them to lead me in the right direction and I find most don’t, they produce readable results but only but working out how to get the fluent interface to do what I want isn’t any easier than with a normal interface in a lot of cases right now.
Nobody wanted to say this, but I will. Fluent Interfaces look goofy and stupid in C#. I’m never confident where to find the building blocks of the API (are they methods of the returned objcet, are they arguments?). I keep saying that we will be embarrassed by the amount of energy we are putting in these monstrosities a few years from now. I also agree with everything Steve commented above.
Personally I find Moq ( http://code.google.com/p/moq/ ) much more intuitive than Rhino Mocks. Substituting lambdas for anonymous delegates might improve readability, too.
I’ve got to agree, most fluent interfaces don’t feel terribly fluent to me, ESPECIALLY those which use simple English words as methods — the Is() And() Not() Or(), as Steve Bohlen mentioned.
Consider two hypotheticals:
Assert.IsTrue(someVal, “Should be true”);
Assert.That(someVal).Is(true).Otherwise(“Should be true”);
When I read code, I want to read CODE, not English words with horrible character delimiting.
Check out the following to get rid of the “standard object cruft like .ToString()”:
http://www.clariusconsulting.net/blogs/kzu/archive/2008/03/10/58301.aspx