I’ve been programming for a very long time. I wasn’t even 10 years old when I got access to computers and that was back around 1975! My first programming experience were on a programmable calculator and later with BASIC on a ZX-81. Around 1985 I started learning other programming languages and around 1988 I even used Turbo Pascal 5.5 which has support for object-oriented programming. Well, a bit limited.
When I started using Turbo Pascal and later Delphi, I also learned the value of methods that would return objects. But it would still take a while before I realized the full power of this. Still, in Turbo Pascal 6 there was a feature called Turbo Vision that could be used to create complete menu structures and dialog screens on a text console! It made extensive use of methods returning objects to make method chaining possible.
Method chaining is a simple principle. You have a class and the class has methods. And each method can return an object of a specific class. So that gives access to a second method. And a third, a fourth, a fifth until you get to a method that returns void.
That’s a dead stop there!
I recently worked on a project where I used a long method chain to keep a clear workflow visible in my code. And my code was selecting data, ordering it, manipulating it and then saved it to a comma-separated file or CSV file. Something like this:
MyData
.ToList()
.OrderBy(SortOrder)
.Take(50)
.Select(NewDataFormat)
.SaveToCSV(Filename);
But SaveToCSV was a method that returned void, so the chain breaks there! But I wanted to continue the workflow as I needed to do more with this data. I also wanted to display it on screen, save it to a database or even filter it a bit more. So, to resolve this I needed a better method that would allow me to continue the chain.
Of course, I could also have written my code like this:
var myList = MyData.ToList();
var mySortedList - myList.OrderBy(SortOrder);
var myTop50 = mySortedList.Take(50);
var myNewList = myTop50.Select(NewDataFormat);
myNewList.SaveToCSV(Filename);
This code would allow me to also continue the workflow and would also allow me to use myNewList
for further processing. But it also introduces a bunch of variables and it allows non-related code to be included within these lines, obscuring the workflow! That would not work! The chain of work would be broken by irrelevant tasks.
So, now I have two options. Either I modify the SaveToCSV
method so it will return a value (preferably the object itself) or I make an extension method inside a static class that would allow me to put a void method within my chain. And I came up with this beautiful, yet simple method:
public static T Do<T>(this T data, Action<T> action){ action(data); return data; }
And this simple, yet beautiful construction is a generic method that can be used for any class, any object! And it can change my code into this:
MyData
.ToList()
.OrderBy(SortOrder)
.Take(50)
.Select(NewDataFormat)
.Do(d=>d.SaveToCSV(Filename))
.Do(d=>Console.WriteLine(d));
Now my data flow is still intact and the flow of the code is still very readable. It is easy to see that this is just one block of code. Most people tend to forget that programming isn’t about writing code. It’s about how to process data.
Using a method chain is a perfect way to visualize data flow inside your code. But to allow proper method chaining, each method will have to return some kind of object for further processing. Otherwise, you will need a Do<> extension method in your project to make a void method part of the chain.
But to keep it simple, when using a void method, you’ll basically put a stop to the data flow within your application. You would then have to start a new data flow for further processing. This is okay, but generally not the best design in programming. While not everyone might be a fan of method chaining, it still is a very powerful way to write code as it forces you to keep irrelevant code outside of the flow.
Pingback: Fizz Buzz gets chained… | Wim ten Brink