Fluent Code in C#

Comments 0

Share to social media

One of the more interesting by-products of LINQ is the interest it has generated in the use of ‘fluent code’. But what is “fluent code”? In this article, I’ll briefly introduce the theory before exploring this popular programming paradigm. We will discuss some techniques and considerations for implementing a fluent interface in the enterprise, illustrated by a demonstration application

The Fluent Interface

Several years ago, in 2005, the object-oriented programming expert Martin Fowler published his essay on the ‘fluent interface’. He described an approach for building software with more readable code that could be more easily maintained by developers because it is easier to read, and discover how to use, than a traditional API that contains functions with a number of parameters. While the fluent interface might have been an idea ahead of its time when the technique was first published, one now sees terms employed to describe code, such as, “fluid coding”, “fluent style” and “fluent API”, which suggest its time has arrived

What exactly constitutes a fluent interface? Fowler describes the way that processes are defined by creating the various objects and then wiring them up together by means of an internal domain-specific language (DSL). The intention is to produce an API that is readable and flows. He suggests using method chaining, with nested functions and object scoping. There are several approaches to implementing this depending on the language that is used. Java has supplementary classes or libraries to do this. Ruby and Scala have an inbuilt fluent interface. For C# and VB, there is LINQ. The LINQ (Language-Integrated Query) snippet below exemplifies the concept. It converts a few data operations into a statement.

The chaining of several methods to produce an IEnumerable collection stands out as the most interesting characteristic of this query. Before LINQ, a C# programmer who required a list of Customers with recent and large orders would have likely needed to write several lines of code.

From what I’ve said so far, the importance of the underlying context may not be obvious. By definition every chained LINQ command knows that the object it consumes and returns contains IEnumerable data with operations exposed by IQueryable . This understanding of the context allows the programmer to combine several SQL-like methods into one statement for building a dataset. For example, the above Where accesses each OrderList record via IEnumerable‘s GetEnumerator , as well as adding filtering to other commands (OrderBy , Take and Select ) via IQuerable‘s Expression .

Domain knowledge constitutes a subtler, non-technical, requirement for successfully implementing a fluent interface. If the developers who are using the fluent interface are not well versed in knowledge about the business domain, then they are not going to make things simpler to use by combining enigmatic methods. If the domain methods are not understood by the developers before they implement the fluent interface, then they will elucidate nothing by chaining them together. Can you imagine developers who are unacquainted with filtering and grouping data finding comfort with LINQ’s Where and GroupBy methods?

In summary, fluent code typically involves three properties: chained methods; a context; and common knowledge of the business domain.

Fluent Code Demo

While LINQ stimulates interest in fluent interface designs as a code consumer, it does not yield tremendous insight into their implementation. This section walks through enhancing an imaginary preexisting order processing application programming interface (API) by adding a fluent interface.

Why Bother?

I suspect that you will find code resembling this in many enterprises. There isn’t anything particularly wrong with it. Some might rate the supporting API quite highly since it allows developers to process orders without undue fuss.

Despite the reasonableness of this code, it seems awkward for such a well-defined activity as processing orders. Why do developers need to spin up so many objects and write so much code for such a basic activity? Wouldn’t it make it easier to augment the order processing API with a fluent interface? Here is an example of what such a fluent interface might look like.

How

We begin creating our fluent interface with our preexisting order processing API, as well as an appreciation for the enterprise developers’ order processing business knowledge. Based on that information, we can start coding the fluent interface. We stop when our developers find the new interface easier to use than the existing API.

Let’s describe what we need to code, phrased in fluent interface terminology. We need to define the order processing context and then incorporate the chained methods for order processing.

Step 1: Context

Our first attempt at creating the context begins with the new class OrderEngine. Within it, we reference and instantiate the essential order-processing classes from the API as shown below.

Our only significant change from the original order processing code is to add support for augmenting validation via the ValidateFunctions property. As we will shortly see it allows OrderEngine to apply more the current non-null object checks.

At this point one could fairly describe OrderEngine as some form of a factory software pattern. Not until we add supporting extension methods does it achieve fledgling fluent interface status.

Step 2: Chained Methods

The OrderEngineExtension static class includes extension methods that allow developers to easily configure, enhance, and modify an OrderEngine instance. The first two methods establish the customer and what they bought.

While these two methods are simple, they have already made it easier for those developers using the interface to code an order. For example, AddLineItem ensures that the OrderEngine accepts the provided orderLineItem with throwing any exceptions. By adding null checks and automatic instantiation of OrderEngine , developers are subsequently neither obliged to write such code nor to worry about it.

The next extension method, Process, allows us to complete orders. If the code seems familiar, it is. The algorithm essentially mimics that of the original code for processing an order. Our fluent interface simply stuffed the gore into one method that developers no longer fret over.

The following methods go beyond coding the basic order process. They’re in the spirit of building a fluent interface that allows developers to easily alter the default behavior of OrderEngine as expressed in the Initialize method. The first method facilitates swapping out the ITaxCalculator .

The next one allows for incorporating additional custom validation checks when order processing.

Taken together, these two methods simplify the trials and tribulations of working with a new tax feature. For example, imagine some orders require the magnificent TaxCalculatorV2. Unfortunately this incredible ITaxCalculator enhancement may create tax credits, and the business prefers not to lower the order total for any reason. Fortunately, our nascent interface handles this situation quite nicely as shown with orderEngineTaxing .

Step 3: Implicit Conversion (Optional)

There is something about the last code snippet that you can easily have missed: it shows that accessing an Order object is clunky. Why should a developer have to work through an OrderEngine ‘s Order property to get at an Order ? Wouldn’t it be in the spirit of the fluid interface pattern to make such calls more intuitive? We can do this by adding the implicit operator to the Order class as shown below.

Developers may now grab an Order with the down-side of having to abandon the magic of var variable typing.  If that loss seems acceptable, then the below certainly eases their coding burden.

Step 4: Unit Tests (Optional)

Fluent interfaces do not alter the reality of unit testing: Nonetheless, writing tests with their help minimizes the woes of setup and configuration. Wouldn’t developers prefer to code the following test when working with an ITaxCalculator ?

Considerations

‘Coming up with a nice fluent API requires a good bit of thought.’

Martin Fowler 2005

Most “how to” technology articles gloss over real world implementation pitfalls. This one is no exception. However, you’re more likely to encounter a vexing design roadblock when implementing a fluent interface than a technical one. The problem is that the fluent interface is neither design pattern nor framework. It is a style of API; and as such suffers the same challenges you’d face when designing any API. Of course, the fluent interface introduces its own additional flavor of API design challenges.

Imagine our demonstration API several months later. After a few revisions of OrderEngine the typical code for placing an order resembles the following snippet.

Like the non-fluent order processing code shown earlier in this article there is nothing particularly good or bad about the above code. Nonetheless, it suggests a few commonly occurring deficiencies in its fluent-interface API design.

Gratuitous Extensions

API Design Guidelines

If unfamiliar with the challenges of writing an effective application programming interface (API) for .NET, MSDN provides a great starting point. The Cwalina and Abrams “Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries” book remains an impressive reference for beginners and experts alike.

At first glance, the new Configure method with its OrderEngineConfiguration parameter resembles a stroke of genius in object-oriented design. It allows developers to properly configure their instance of OrderEngine . By why do they need to do this? Couldn’t OrderEngine handle such mundane instantiation chores?

It isn’t easy to answer this question without talking to the developer that wrote this new extension method. Maybe without a significant refactoring effort, the act of changing OrderEngine initialization broke too many consuming applications? And maybe the developer didn’t have the time to refactor? Whatever the reason, it’s likely that Configure was unnecessary.

Context Confusion

The last two chained methods raise questions about the revised OrderEngine‘s context. Developers now need to write more code and know more about placing orders then they did with the initial fluent interface. Has the order-processing context become needlessly confusing?

For example, why do developers now need to call UpdateInventory? What happens if they do not include this method? At first glance it looks superfluous, couldn’t a method like Process be called internally. Speaking of Process , why was the enumeration ProcessingOptions.Async added? Does it reflect some new order handling mechanics? If so, should developers care? And if they do care do they need to write different code for a synchronous ProcessingOption option?

Exception Mismanagement

Wrapping OrderEngine in a try/catch block highlights one of the more daunting design challenges of the fluent interface style. How does one handle runtime exceptions? Under the older declarative line-by-line style, developers trapped issues such as dodgy orders. Fluent interfaces typically assume responsibility for such pests. What happens if they don’t catch them?

The OrderEngine‘s response to handling unforeseen validation issues appears to be a try/catch block. One suspects that there is a better solution. For example, developers could validate orders before processing them, or perhaps check the order state afterwards. Nonetheless, both of these tacks demand additional code, despite being esthetically more pleasing.

The Viscous Interface

You may find the latest release of OrderEngine to be a victim of a gratuitous extension, confused context, or mismanaged exception handling. It is certainly safe to question the ease of use of the API. For whatever reason, it seems to have lost its fluid nature. How does a fluent interface transform into a viscous mess?

Field experience suggests that fluent interfaces that are updated without vigilance will degrade over time; so quietly, in fact, that those developers that consume the API do not notice. The enterprise only becomes aware of the problem when developers who are unfamiliar with the API arrive, and complain about unwrapping the abstraction formally known as fluent. By then it’s too late though, the API is another example of code suffering from severe technical debt.

Too Big to Fluent?

The demonstration nature of OrderEngine deprives us the opportunity of implementing one of the more common design failures. Fluent interfaces may grow unwieldy with updates and new requirements to remain developer friendly. The API resembles a version of the infamous god class anti-pattern as the context struggles to handle too many scenarios and situations.

Experience managing configurations via fluent interfaces offers guidance for handling the bloated API. Many of these interfaces apply a “divide and conquer” strategy, a well-recognized refactoring solution to the god class. The following MSDN code snippet offers an example of such a design.

Note how the ConfigurationSourceBuilder instance relies on a dedicated caching fluid interface for easing a complex configuration. Interested readers will find other such specialty configuration fluent interfaces, such as ConfigureCryptograpy, ConfigureData or  ConfigureExceptionHandling for the Enterprise Library.

Conclusion

Our review of fluent interfaces in C# covered a few ideas. First, we discussed the concept as an API style for improving the development experience. Second, that while fluent interface is a new idea, it is not technically demanding. And lastly, what building a fluent interface lacks in technical complexity it makes up for in design difficulty.

Load comments

About the author

Tom Fischer

See Profile

Tom Fischer designs and builds software solutions upon the Microsoft stack. And every so often he writes and presents some of the things he's learned in his trade over the past dozen years.

Tom Fischer's contributions