Fluent Interfaces

Up till quite recently I had never heard of Fluent Interfaces and even though Martin Fowler coined the term in 2005 The only exposure I have had with them has been while using Rhino Mocks, here is an example for those not familiar with this mocking framework:

    Expect.Call(mockSearchEngine.Results)
             .Repeat.AtLeastOnce()
             .IgnoreArguments();

As you can see the idea behind them is to present an API that reads like a sentence in the domain language of the objects involved.

Controversy

There appears to be a lot of divided opinion in the dev community to there usage and doing a quick google will no doubt bring back results for both the positive and negatives of using them.

An Example

In order to get a feel as to how Fluent Interfaces could be applied I came up with an example usage against a StringTokenizer class I developed while writing the multi search engine code, below is a class diagram to get an idea of the structure:

Fluent Interface Class Diagram

These classes were fleshed out TDD style using the below unit tests:

[Test]
public void Should_replace_matched_single_string_on_output()
{
    string output = new Pattern( "string  match on" )
                                    .Replace( "" ).With( "to" )
                                    .Output();

    Assert.AreEqual( "string to match on", output);
}

[Test]
public void Should_replace_matched_multiple_strings_on_output()
{
     string output = new Pattern( " string to  on" )
                                  .Replace( "" ).With( "bigger" )
                                  .Replace( "" ).With( "match" )
                                  .Output();

     Assert.AreEqual( "bigger string to match on", output);
}

[Test]
public void Should_not_match_any_strings_on_output()
{
    string output = new Pattern( "string  match on" )
                                   .Replace( "" ).With( "to" )
                                   .Output();

    Assert.AreEqual( "string  match on", output);
}

[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void Should_throw_exception_if_pattern_string_is_null_on_construction()
{
     new Pattern(null);
}

[Test]
public void Should_return_input_string_if_no_replaces_provided_on_output()
{
    string output = new Pattern( "test string" ).Output();
    Assert.AreEqual( "test string", output);
}

TDD is the obvious choice when trying to design the Fluent Interface as you try to come up with a good API.Update: code example can now be downloaded from planet source code

My Thoughts

  • One thing that stands out is the weird naming of classes/interfaces you end up with, which make perfect sense from outside but are not quite what were used internally, the argument could be made that by making the API easier to use externally it compromises the understanding of the code internally.
  • Before using a Fluent Interface I already had the StringTokenizer class in place, and had to make no changes to this class, In most cases the Fluent Interface becomes a Facade which will delegate to a Builder object as is the case in this example.
  • Even for this simple example I ended up with 2 new interfaces and 3 new classes, it’s clear that for a complicated case a lot of extra work will need to be done just for the API. Therefore there must be a strong argument for deciding to provide a Fluent Interface as opposed to having a standard Builder object

Enhancement (Kinda!)

[Test]
public void For_tokenizer_should_replace_matched_single_string_on_output()
{
    string output = Tokenizer.For( "string  match on" )
                                      .Replace( "" ).With( "to" )
                                      .Output();

    Assert.AreEqual("string to match on", output);
}
Advertisements