Today, Vin just tweeted "FI(Fluent Interface) - when you use method chaining, after finishing the functionality, "return this", and that's how you make it fluent".
I thought about writing a detailed post on this, just to demonstrate various possibilities of Fluent programming.
Chaining Methods - A Simple Scenario
Assuming that you are interested in training animals, let us start with a simple ITrainable interface
. :)
public interface ITrainable
{
ITrainable Train(string skill);
ITrainable Do(string skill);
}
Well, nothing fancy there. Let us go ahead and create a Dog
class, which implements this interface
. The only interesting piece you may find in the Dog
class is, all methods are returning a type of ITrainable
. As long as our Dog
class implements ITrainable
, we can return 'this
', i.e. the current Dog
Instance.
public class Dog : ITrainable
{
public string Name { get; set; }
public List<string> Skills { get; set; }
public Dog(string name)
{
Console.WriteLine();
Console.WriteLine("Dog " + name +
" created");
Name = name;
Skills = new List<string>();
}
public ITrainable Train(string skill)
{
Console.WriteLine("Dog " + Name +
" learned " + skill);
this.Skills.Add(skill);
return this;
}
public ITrainable Do(string skill)
{
if (Skills.Contains(skill))
Console.WriteLine("Dog " + Name +
" is doing " + skill);
else
Console.WriteLine("Dog " + Name +
": Don't know how to " + skill);
return this;
}
}
Now, we are ready to train our Dog
fluently. Like:
var dog = new Dog("Bobby");
dog.Train("Running").Train("Eating")
.Do("Running").Do("Eating");
As you can see, we are chaining the method calls, because in each call, we are returning an object of type ITrainable
. You'll see what Bobby is doing in the console:
Dog Bobby created
Dog Bobby learned Running
Dog Bobby learned Eating
Dog Bobby is doing Running
Dog Bobby is doing Eating
Chaining Methods - For Collections
Now, let us do something more interesting. Let us create a couple of extension methods for all collections of ITrainable
. We do this by writing an extension method for IEnumerable<ITrainable>
. If you see, our extension methods are accepting a bunch of trainable organisms(?) (read, IEnumerable<ITrainable>
) and return the same.
Leave out the Console.WriteLine()
, it is there just for some pretty printing.
public static class TrainableExtensions
{
public static IEnumerable<ITrainable>
Train(this IEnumerable<ITrainable> flock, string skill)
{
foreach (var member in flock)
member.Train(skill);
Console.WriteLine();
return flock;
}
public static IEnumerable<ITrainable>
Do(this IEnumerable<ITrainable> flock, string skill)
{
foreach (var member in flock)
member.Do(skill);
Console.WriteLine();
return flock;
}
}
Now, let us create few dogs
, and train them together. :)
var dogs = new List<ITrainable>{new Dog("Jimmy"),
new Dog("Sando"),
new Dog("Rob")};
dogs.Train("EatingFood").Train("Running")
.Do("EatingFood")
.Train("JumpingToRing").Do("Running");
And you'll see what they are doing, in this order.
Dog Jimmy created
Dog Sando created
Dog Rob created
Dog Jimmy learned EatingFood
Dog Sando learned EatingFood
Dog Rob learned EatingFood
Dog Jimmy learned Running
Dog Sando learned Running
Dog Rob learned Running
Dog Jimmy is doing EatingFood
Dog Sando is doing EatingFood
Dog Rob is doing EatingFood
Dog Jimmy learned JumpingToRing
Dog Sando learned JumpingToRing
Dog Rob learned JumpingToRing
Dog Jimmy is doing Running
Dog Sando is doing Running
Dog Rob is doing Running
Now, as LINQ methods are essentially extension methods on top of IEnumerable<T>
, you can mix and match the extension methods we just wrote, with LINQ methods. For example, if we decide only Rob should learn a special skill (JumpingRing
), you can do this.
dogs.Train("EatingFood").Skip(2)
.Train("JumpingRing").Union(dogs)
.Train("TakingPaper");
And you'll see how this works:
Dog Jimmy learned EatingFood
Dog Sando learned EatingFood
Dog Rob learned EatingFood
Dog Rob learned JumpingRing
Dog Rob learned TakingPaper
Dog Jimmy learned TakingPaper
Dog Sando learned TakingPaper
And finally, Fluent
APIs can be used for much more useful tasks (If you haven't yet realized, LOL). Few fluent APIs I've come across are:
Happy coding!!
CodeProject