A funny thing in programming, captured in a term by Martin Fowler and Eric Evans, is fluent interface.
Explained shortly, it is a style in programming which attempts to make your programs (or parts thereof) resemble sentences as you could (but probably seldomly would) have pronounced them in a fairly understandable human language.
It is easier in dynamic languages like Ruby, but it is still possible in a staticly bound world like that of C# – and it is often a fun challenge to design your utility classes or domain logic to use fluent interface! Moreover, it has a legitimate use in implementing internal domain-specific languages because of its resemblance to human language.
A small example of a list converter class using some kind of fluent interface can be seen if you carry on reading below.
The class(es):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
namespace Heller.FluentInterface { public static class CollectListOf<T> where T:class { public static FromEachClass<U> FromEach<U>() { return new FromEachClass<U>(); } public class FromEachClass<U> { public delegate T ConversionHandler(U u); public InTheCollectionClass InTheCollection(IEnumerable<U> collection) { return new InTheCollectionClass(collection); } public class InTheCollectionClass { IEnumerable<U> collection; bool dontAddNull; public InTheCollectionClass(IEnumerable<U> collection) { this.collection = collection; } public InTheCollectionClass UnlessItIsNull() { dontAddNull = true; return this; } public List<T> LikeThis(ConversionHandler convert) { List<T> newList = new List<T>(); foreach (U u in collection) { T t = convert(u); if (dontAddNull && t == null) continue; newList.Add(t); } return newList; } } } } } |
The source code above may seem cryptic at first. However, there is a pattern in the fact that another class instance is returned every time the language syntax requires another type of sentence to be “syntactically correct” (human language-wise). The only time this is returned, it is because UnlessItIsNull is sort of an interjected sentence.
We need an example of its usage. It comes in the form of an NUnit test:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
namespace Heller.FluentInterface { [TestFixture] public class TestListUtilities { [Test] public void MakeAndUseList() { IList<FileInfo> fileInfos = new List<FileInfo>(new FileInfo[]{ new FileInfo("C:\\autoexec.bat"), new FileInfo("C:\\config.sys"), new FileInfo("C:\\this_file_does_probably_not_exist.foo") }); List<string> namesOfExistingFiles = CollectListOf<string> .FromEach<FileInfo>() .InTheCollection(fileInfos) .UnlessItIsNull() .LikeThis(delegate(FileInfo fileInfo) { return fileInfo.Exists ? fileInfo.Name : null; }); Assert.AreEqual("autoexec.bat", namesOfExistingFiles[0]); Assert.AreEqual("config.sys", namesOfExistingFiles[1]); } } } |
Btw, I am aware that the List class already has a ConvertAll<T> method, which esentially does the same. One justification of writing your own class like this could be that your collections come in forms other than List<T>. This class takes an IEnumerable<T>, which is implemented by all collections (that I know of) in .NET.