Fluent Interfaces – Method Chaining

The core idea behind building a fluent interface is one of readability – someone reading the code should be able to understand what is being achieved without having to dig into the implementation to clarify details.

In modern OO languages such as C#, VB.NET and Java, method chaining is one way that this is achieved, but it’s not the only technique – two others are factory classes and named parameters.

Note also that these techniques are not mutually exclusive – the goal is to maximize readabilty of the code, not purity of approach.

Method Chaining

The key insight behind method chaining is to never have a method that returns void, but to always return some object, or more often, some interface, that allows for further calls to be made.

You don’t need to necessarily return the same object on which the method was called – that is, you don’t always need to “return this;”.

One useful design technique is to create an inner class – I always suffix these with “Expression” – that exposes the fluent API, allowing for configuration of another class.

This has two advantages – it keeps the fluent API in one place, isolated from the main functionality of the class, and (because it’s an inner class) it can tinker with the innards of the main class in ways that other classes cannot.

You may want to use a series of interfaces, to control which methods are available to the developer at a given point in time.

Factory Classes

Sometimes you want to build up a series of related objects – examples include the NHibernate Criteria API, Rhino.Mocks expectation constraints and NUnit 2.4’s new syntax.

In both of these cases, you have the actual objects you are storing, but to make them easier to create there are factory classes providing static methods to manufacture the instances you require.

For example, in NUnit 2.4 you can write:

Assert.That( result, Is.EqualTo(4));

The “Is” class is a static class full of factory methods that create constraints for evaluation by NUnit.

In fact, to allow for rounding errors and other imprecision of floating point numbers, you can specify a precision for the test:

Assert.That( result, Is.EqualTo(4.0).Within(0.01));

(Advance apologies – my syntax may be off.)

Named Parameters

In languages that support them (including Smalltalk, and C# 4.0) named parameters provide a way to include additional “syntax” in a method call, improving readability.

Consider a hypothetical Save() method that takes a file name, and permissions to apply to the file after saving:

myDocument.Save("sampleFile.txt", FilePermissions.ReadOnly);

with named parameters, this method could look like this:

myDocument.Save(file:"SampleFile.txt", permissions:FilePermissions.ReadOnly);

or, more fluently:

myDocument.Save(toFile:"SampleFile.txt", withPermissions:FilePermissions.ReadOnly);

Leave a Comment