Thursday, May 5, 2011

A Fluent Backwards Design

Fluent interfaces make code easier to read, understand, and use. They can also provide a layer of separation and decoupling to make it easier to reuse components in a straightforward fashion. In this post I will walk through the scenario of creating a fluent interface with a "backwards design." This means I'll start with the end in mind: what I want my interface to look like, and build the API to support that.

The scenario is a common one that illustrates the point nicely: a feed reader. Regardless of using a fluent interface, you are likely to be following the MVVM model and practice SOLID principles. (If not, please read the linked article for a jumpstart).

The individual feed item can be modeled simply like this:

public class FeedItem
{
    public string Id { get; set; }

    public DateTime Posted { get; set; }

    public string Title { get; set; }

    public string Description { get; set; }
}

Knowing the definition of the feed item now makes it possible to create a view model to host it:

public partial class ViewModel
{
    public ViewModel()
    {
        FeedItems = new ObservableCollection<FeedItem>();

        DesignData();

        if (DesignerProperties.IsInDesignTool) return;
    }

    public ObservableCollection<FeedItem> FeedItems { get; private set; }
}

The design data can then populate the sample feeds:

public partial class ViewModel
{        
    [Conditional("DEBUG")]
    protected void DesignData()
    {
        if (!DesignerProperties.IsInDesignTool)
        {
            return;
        }

        for (var x = 0; x < 20; x++)
        {
            var feedItem = new FeedItem();
            feedItem.Id = Guid.NewGuid().ToString();
            feedItem.Posted = DateTime.Now.AddDays(-20 + x);
            feedItem.Title = "Blog Post Entry";
            feedItem.Description =
                "This is a sample blog post entry for the purpose of design-time data on the design surface.";
            FeedItems.Add(feedItem);
        }
    }
}

This simply wires up a short list for data-binding. With some simple XAML, the entire structure is complete:

<Grid x:Name="LayoutRoot" Background="White">
    <Grid.DataContext>
        <FluentBackwardsDesign:ViewModel/>
    </Grid.DataContext>
    <ListBox ItemsSource="{Binding FeedItems}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock FontWeight="Bold" Text="{Binding Posted}"/>
                        <TextBlock Text="{Binding Title}" FontStyle="Italic" HorizontalAlignment="Right" Margin="20 0 0 0"/>                            
                    </StackPanel>
                    <TextBlock TextWrapping="Wrap" Grid.Row="1" Text="{Binding Description}"/>
                </Grid>                    
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>

This project is set as a Silverlight OOB application with elevated trust to avoid any cross-domain issues with loading the feed. To load the feed requires several steps:

  1. Initiate the request to fetch the feed using the WebClient
  2. Receive the response
  3. Stuff the response into a string
  4. Pass the string into an XmlReader
  5. Pass the XmlReader into the SyndicationFeed
  6. Iterate the feeds and populate the feed items

The non-fluent way to perform these steps may look something like this:

protected void OldWay()
{
    var wc = new WebClient();
    wc.DownloadStringCompleted += _WcDownloadStringCompleted;
    wc.DownloadStringAsync(_targetUri);
}

void _WcDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
    ((WebClient) sender).DownloadStringCompleted -= _WcDownloadStringCompleted;

    using (var stringReader = new StringReader(e.Result))
    {
        using (var reader = XmlReader.Create(stringReader))
        {
            var feed = SyndicationFeed.Load(reader);
            foreach(var feedItem in feed.Items.Select(
                item => new FeedItem
                            {
                                Id = item.Id,
                                Posted = item.PublishDate.Date,
                                Title = item.Title.Text,
                                Description = item.Summary.Text
                            }))
            {
                FeedItems.Add(feedItem);
            }
        }
    }
}

This code adds a lot to the view model. It violates the Single Responsibiity Principle by requiring the view model to understand how to fetch the data, how to handle the return, how to parse it and then feed it in. The various steps are also not reusable or unit testable as they depend on the live feed and connection.

Good practices would dictate moving away from this model to decoupled classes with separated responsibilities. Why not go ahead and make this fluent?

Ideally, the pattern of opening some data (whether a feed or other format), parsing it and populating a list will be easy and fluent. Starting with the end in mind, what if the code looked like this?

ServiceLocator
    .GetHelper<FeedItem>()
    .For(FeedItems)
    .From(_targetUri)
    .ParseWith(new FeedParser())
    .Go();

This would be all of the code necessary to make the view model work: get a helper for the type of list, indicate what collection should be populated, provide the target URI, inject the strategy to map the format to a known entity, and kick it all off.

The model for fluent extensions is to provide a method on the class (whether a local method or an extension method) that ultimately returns itself. This allows for chaining of multiple method calls because each call returns the class to make it available for the next. Knowing this, the contract for the helper can be defined like this:

public interface IWebClientHelper<T>
{
    IWebClientHelper<T> For(ICollection<T> target);

    IWebClientHelper<T> From(Uri uri);
        
    IWebClientHelper<T> ParseWith(IParser<T> parse);
        
    void Go();
}

The parser contract can also be inferred, because it would take in the string resulting from the download and return the collection.

public interface IParser<T> 
{
    IEnumerable<T> Parse(string src);
}

Now it's possible to implement the parser. The implementation is nice because it can be unit tested by supplying a predefined string and comparing against the expected output:

public class FeedParser : IParser<FeedItem>
{
    public IEnumerable<FeedItem> Parse(string src)
    {
        using (var stringReader = new StringReader(src))
        {
            using (var reader = XmlReader.Create(stringReader))
            {
                var feed = SyndicationFeed.Load(reader);
                return feed.Items.Select(
                    item => new FeedItem
                                {
                                    Id = item.Id,
                                    Posted = item.PublishDate.Date,
                                    Title = item.Title.Text,
                                    Description = item.Summary.Text
                                });
            }
        }
    }
}

The implementation of the web helper can store the stateful information about the call, then generically implement the call:

public class WebClientHelper<T> : IWebClientHelper<T>
{
    private Uri _uri;

    private ICollection<T> _collection;

    private IParser<T> _parser;        

    public IWebClientHelper<T> For(ICollection<T> target)
    {
        _collection = target;
        return this;
    }

    public IWebClientHelper<T> From(Uri uri)
    {
        _uri = uri;
        return this;
    }

    public IWebClientHelper<T> ParseWith(IParser<T> parse)
    {
        _parser = parse;
        return this; 
    }

    public void Go()
    {
        if (_uri == null)
        {
            throw new ArgumentException("Uri must be set.");
        }

        if (_parser == null)
        {
            throw new ArgumentException("No parser was specified.");
        }

        if (_collection == null)
        {
            throw new ArgumentException("No collection was specified.");
        }

        var wc = new WebClient();
        wc.DownloadStringCompleted += _WcDownloadStringCompleted;
        wc.DownloadStringAsync(_uri);            
    }

    private void _WcDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
    {
        ((WebClient) sender).DownloadStringCompleted -= _WcDownloadStringCompleted;

        foreach (var item in _parser.Parse(e.Result))
        {
            _collection.Add(item);
        }
    }
}

This could be abstracted even further by providing a strategy for the web client download so that process can be mocked as well.

Finally, instead of dealing directly with the helper, a typical implementation would provide some sort of Dependency Injection/Inversion of Control to fire this off. This example simulates that using a simple service locator:

public static class ServiceLocator
{
    public static IWebClientHelper<T> GetHelper<T>()
    {
        return new WebClientHelper<T>();
    }
}

Anything that depends on the fluent object can now be easily mocked as well, so the fluent interface is perfectly testable. And that's it ... working with the end in mind, this exercise simplified the view model and provided a reusable, fluent interface for grabbing information from remote websites and parsing it into typed collections.

Download the source here.

Jeremy Likness

10 comments:

  1. This is an awesome guide to creating a Fluent interface. Good stuff.

    ReplyDelete
  2. One thing I do differently is how I ensure that people call the methods in the correct order. Rather than relying upon guard clauses and exceptions, I use the type system. Rather than have the IWebClientHelper interface always return IWebClientHelper, I'll have it return progressively more knowledgeable types.

    For example, IWebClientHelper.For returns IWebClientHelperWithTarget. IWebClientHelperWithTarget.From returns IWebClientHelperWithSource. IWebClientHelperWithSource.ParseWith returns IWebClientHelperWithParser. Finally, IWebClientHelperWithParser declares the Go method.

    The advantage of this approach is two-fold. First, intellisense guides the user into doing the right thing. Second, you can prove at compile time that the user has done the right thing, so you can get rid of the runtime checks. I think of guard clauses like a game of Operation, buzzing at the consumer if they make a mistake.

    I’ve written about this technique in the following posts:
    http://adventuresinsoftware.com/blog/?p=112
    http://qedcode.com/practice/provable-apis

    ReplyDelete
  3. Beautiful stuff. Quite honestly one of the best posts you've written. Thanks very much :-)

    ReplyDelete
  4. An alternative to using a fluent interface is using optional parameters. Obviously this won't work for complex scenarios but it can save a lot of code for simple ones.

    ServiceLocator
    .GetHelper(
    for: FeedItems,
    from: _targetUri,
    parseWith: new FeedParser()
    ).Go();

    ReplyDelete
  5. I've written a toolkit that takes a state machine written in Dot (http://www.graphviz.org/) and then generates the appropriate C# code for you.

    I totally agree with Michael about using the type system to make sure people call things in the right order, and this state machine based fluent interface makes it easy, IMHO.

    The project is at http://flit.codeplex.com/ - it might seem like overkill for starters, but it's really quite simple to get up and running with, it's just a T4 based template. Check out the samples in the source...

    ReplyDelete
  6. One thing threw me off as I was reading. The ViewModel constructor has an if statement with a return. Is this left over from before you were using the Conditional Debug? Or am I missing something?

    ReplyDelete
  7. It's a common practice I have to delineate where the "design-time" ends, so any run time code can naturally fall after that statement.

    ReplyDelete
  8. Very good article Jeremy. I particularly like the approach to making this unit testable without introducing too much in the way of abstraction.

    ReplyDelete
  9. Like your post(s). Question: do you have any suggestions as to when to use fluent design and when not? I love using it..but I can see it potentially being used when it doesnt add value.

    ReplyDelete
  10. It is simple for me - I code using non-fluent APIs and the instant the code becomes unreadable, confusing, or complex (i.e. lots of nested blocks and methods just to make it happen), I begin to work on a more fluent API to make it more easily understandable and useable.

    ReplyDelete