Click here to Skip to main content
15,890,557 members
Articles / Desktop Programming / WTL

Guidelines to Fluent Interface design in C# - Part 2

Rate me:
Please Sign up or sign in to vote.
4.60/5 (3 votes)
5 Aug 2010CPOL3 min read 13.7K   13   1
Some techniques to code each part of the sentence with some samples.

Shortcut: http://tiny.cc/h7c5r
Part 1: http://tiny.cc/rpj99

In part 1, I've mentioned the history, the basic concepts of Fluent Interfaces and a small glossary of terms that I will use in this series of posts. Now I will start discussing about how to code it.

Let’s begin defining a small set of objects that I will use in our samples. It’s just the basic properties for now, but these objects will evolve during this series.

C#
public interface IClient {
    int Id { get; }
    string Name { get; set; }
    string Phone { get; set; }
    string Address { get; set; }
}

public partial class Client : IClient {
    private int _id;

    public Client(int id) {
        _id = id;
    }

    public int Id { get { return _id; } }
    public string Name { get; set; }
    public string Phone { get; set; }
}

public partial interface IProduct {
    int Id { get; }
    string Name { get; set; }
    string Color { get; set; }
    double Price { get; set; }
}

public partial class Product : IProduct
{
    private int _id;

    public Product(int id) {
        _id = id;
    }

    public int Id { get { return _id; } }
    public string Name { get; set; }
    public string Color { get; set; }
    public double Price { get; set; }
}

public interface IOrderItem {
    IOrder Order { get; }
    int Number { get; }
    IProduct Product { get; set; }
    double Price { get; set; }
    double Quantity { get; set; }
}

public partial class OrderItem : IOrderItem {
    private IOrder _order;
    private int _number;

    public OrderItem(IOrder order, int number) {
        _order = order;
        _number = number;
    }

    public IOrder Order { get { return _order; } }
    public int Number { get { return _number; } }
    public IProduct Product { get; set; }
    public double Price { get; set; }
    public double Quantity { get; set; }
}

public interface IOrder {
    int Id { get; }
    DateTime Date { get; set; }
    IClient Client { get; set; }

    IEnumerable<IOrderItem> Items { get; }
}

public class Order : IOrder {
    private int _id;

    public Order(int id) {
        _id = id;
        Date = DateTime.Now;
        Items = new List<IOrderItem>();
    }

    public int Id { get { return _id; } }
    public DateTime Date { get; private set; }
    public IClient Client { get; set; }
    public IEnumerable<IOrderItem> Items { get; private set; }
}

Notice that I made the classes partial so we can add code to them later. You can also use extension methods. Here is a sample of both approaches:

C#
public interface IFluentOrder : IOrder {
    IFluentOrder ToClient(string named);
}

// adding code to a partial class
public partial class Order : IFluentOrder {
    public IFluentOrder ToClient(string named) {
        /* Method implementation */
        return this;
    }
}

// using extension methods
public static class OrderExtensions {
    public static IFluentOrder ToDeliver(this IFluentOrder order, string on) {
        order.DeliverDate = Convert.ToDateTime(on); // Make the conversion 
					// here to improve final readability
        /* Method implementation */
        return order;
    }
}

The choice between then will depend on you or on how the object can be extended.

Anyway, my first tip/good practice advice would be:

1 – As much as you can, detach the Fluent interface implementation from the object’s core implementation.

That will allow you to preserve the core of the object and do not mess with structures required to interface with, for instance, the entity framework. Using the extension methods you will even be able to add a Fluent interface behavior to pre-existing objects.

Now let’s define our first sentence:

Place an order to John Doe with 30 yellow pencils and 20 blue pens to be 
delivered on December 25th of 2010 and give me the final price.

Quite simple to speak, isn’t it? But as a developer, you probably already saw some catches in how to code it in a readable way. But let’s go step by step.

Starting the Sentence

There are a few ways to start a sentence.
The most obvious way is to use an existing instance of an object. Like this:

C#
static void Main(string[] args) {
    // ...
    var order = new Order(3);
    order.ToClient("John Doe").ToDeliver("25/Dec/2010")/* Other methods here*/;
    // ...
}

Or you can define a function that creates the object in the scope you are:

C#
class Program {
    private static IFluentOrder CreateOrderWith(int id) {
        return new Order(id);
    }

    static void Main(string[] args) {
        // ...
        CreateOrderWith(id: 3).ToClient(named: "John Doe").
		ToDeliver(on: "25/Dec/2010")/* Other methods here*/;
        // ...
    }
}

Here I used a feature that was made available in C# only after version 4.0: Named Parameters. This feature increases in a great deal the readability of some methods. In the previous version of C#, we would have to write CreateOrderWith(3) hiding the parameter’s name and making this statement a little confusing (Create order with 3 what?). It becomes even more confusing if you have other optional parameters. Other languages like Lisp or Python already have this feature. For those languages that do not support it, there are a few workarounds.

Here is a sample for Java:

Java
string name; int id; boolean overwrite; 	// declare the variables but 
					// do not assign any value
someMethod(name="John", id=47, overwrite=true); // assign the value in method's call

So here is my second tip/good practice advice:

2 – If you can, prefer to use named parameters to clarify the meaning of a method’s input and allow a shorter method’s name.

I will discuss more about the use of Optional Parameters X Object’s Methods to set values in a later post, so let’s go back to object creation.

You can also create a static method that creates the instance directly from its type:

C#
public partial class Order {
    public static IFluentOrder CreateNewWith(int id) {
        /* Method implementation */
        return new Order(id);
    }
}

class Program {
    static void Main(string[] args) {
        // ...
        Order.CreateNewWith(id: 3).ToClient(named: "John Doe").
		ToDeliver(on: "25/Dec/2010")/* Other methods here*/;
        // ...
    }
}

Obs: There is another way that you can use in some circumstances. It is the use of a builder delegate. But I will mention it in a later post.

Which of the Above Approaches is the Best?

That will depend on you, your app architecture or the features available to the language you are using. All three deliver what we need: The object instance that will start the sentence.

Well I think this is enough for today.
Our proposed sentence is not yet fully implemented, but we will keep working on it.
In my next 2 posts, I will talk more about the terms of the sentence, parameters, nesting and explore some alternatives.

Here is a link to a small VS2010 project with the samples mentioned in this post.

Stay tuned.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Pronto Solutions
Brazil Brazil
André Vianna is a Software Engineer in Rio de Janeiro, Brasil.
He is PMP, MCP and have worked as Development Project Manager, Senior Software Developer and Consultant for more than 15 years.
He worked with several major companies in Brasil, EUA, Canada and Mexico.
He has been working with the cut edge .NET platform since 2001 and is a C# specialist.

Comments and Discussions

 
GeneralGreat article! Pin
Andrew Golik7-Sep-10 2:14
Andrew Golik7-Sep-10 2:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.