Cascade pattern / Method chaining
Today, I am going to explain a simple programming pattern step by step.
Often the cascade pattern can be found in connection with consecutive data manipulation. It has many names. In C# I would put it in the same chapter as Extension Methods.
But let us start at the very beginning. Genesis 🙂
This is a simple integer calculation.
int r0 = 5; r0 = ((((r0 + 2) * 4) - 8) / 2) + 1; Console.WriteLine("Result is " + r0); // 11
We could break it down into many separate calculation steps …
int r1 = 5; r1 += 2; r1 *= 4; r1 -= 8; r1 /= 2; r1 += 1; Console.WriteLine("Result is " + r1); // 11
… and build a class to perform reoccurring calculations.
public class Classic { static public int add(int a, int b) { return a + b; } static public int sub(int a, int b) { return a - b; } static public int mul(int a, int b) { return a * b; } static public int div(int a, int b) { return a / b; } } // class int r2 = 5; r2 = Classic.add(r2, 2); r2 = Classic.mul(r2, 4); r2 = Classic.sub(r2, 8); r2 = Classic.div(r2, 2); r2 = Classic.add(r2, 1); Console.WriteLine("Result is " + r2); // 11
Well, this is nice. But the code is clumsy. We could overload operators. Anyhow, this is not the way I would like to go today. What about a memory variable in form of a property called “Result”.
public class Cascade1 { public int Result { get; private set; } public Cascade1(int x) { Result = x; } public Cascade1 add(int x) { Result += x; return this; } public Cascade1 sub(int x) { Result -= x; return this; } public Cascade1 mul(int x) { Result *= x; return this; } public Cascade1 div(int x) { Result /= x; return this; } } // class int c1 = new Cascade1(5) .add(2) .mul(4) .sub(8) .div(2) .add(1) .Result; Console.WriteLine("Result is " + c1); // 11
This looks much better, doesn’t it?
We replace the integers by a simple class, which only has one public field called “Age”.
public class Data { public int Age; } // class
We add IENumberable as parameter. Now, the program looks neat and more flexible.
public class Cascade2 { public static void add(IEnumerable<Data> xIENumberable, int xValue) { foreach (Data x in xIENumberable) x.Age += xValue; } public static void sub(IEnumerable<Data> xIENumberable, int xValue) { foreach (Data x in xIENumberable) x.Age -= xValue; } public static void mul(IEnumerable<Data> xIENumberable, int xValue) { foreach (Data x in xIENumberable) x.Age *= xValue; } public static void div(IEnumerable<Data> xIENumberable, int xValue) { foreach (Data x in xIENumberable) x.Age /= xValue; } } // class List<Data> lList = new List<Data>(); for (int i = 0; i < 10; i++) lList.Add(new Data() { Age = i }); Cascade2.add(lList, 2); Cascade2.mul(lList, 4); Cascade2.sub(lList, 8); Cascade2.div(lList, 2); Cascade2.add(lList, 1); Console.Write("Array result is "); lList.ForEach(x => Console.Write(x.Age + " "));
But once again we face too many parameters. Should we add a memory field? No, C# offers Extension Methods. This is the way to go!
public static class Cascade3 { public static IEnumerable<Data> add(this IEnumerable<Data> xIENumberable, int xValue) { foreach (Data x in xIENumberable) x.Age += xValue; return xIENumberable; } // public static IEnumerable<Data> sub(this IEnumerable<Data> xIENumberable, int xValue) { foreach (Data x in xIENumberable) x.Age -= xValue; return xIENumberable; } // public static IEnumerable<Data> mul(this IEnumerable<Data> xIENumberable, int xValue) { foreach (Data x in xIENumberable) x.Age *= xValue; return xIENumberable; } // public static IEnumerable<Data> div(this IEnumerable<Data> xIENumberable, int xValue) { foreach (Data x in xIENumberable) x.Age /= xValue; return xIENumberable; } // } // class List<Data> lList = new List<Data>(); for (int i = 0; i < 10; i++) lList.Add(new Data() { Age = i }); lList.add(2) .mul(4) .sub(8) .div(2) .add(1); Console.Write("Array result is "); lList.ForEach(x => Console.Write(x.Age + " "));
Each List object can now use the extension methods. In C# you most likely have come across such pattern when using the namespace System.Linq. Adding this namespace automatically enables many methods for lists (IENumerable) and arrays. Here is an example of that namespace. You can chain together methods. Make sure the return parameter points to the same list each time, otherwise the chain is broken. For instance a Sum() would return a number rather an IENumerable, thus breaking the chain.
List<Data> lList = new List<Data>(); for (int i = 0; i < 10; i++) lList.Add(new Data() { Age = i }); // using System.Linq; List<Data> lResult = lList.Where(x => x.Age % 2 == 0).Take(10).ToList(); Console.Write("Array result is "); lResult.ForEach(x => Console.Write(x.Age + " "));
And here is the entire source code in one piece.
using System; using System.Collections.Generic; using System.Linq; namespace CascadePattern { public class Data { public int Age; } // class public static class Cascade3 { public static IEnumerable<Data> add(this IEnumerable<Data> xIENumberable, int xValue) { foreach (Data x in xIENumberable) x.Age += xValue; return xIENumberable; } // public static IEnumerable<Data> sub(this IEnumerable<Data> xIENumberable, int xValue) { foreach (Data x in xIENumberable) x.Age -= xValue; return xIENumberable; } // public static IEnumerable<Data> mul(this IEnumerable<Data> xIENumberable, int xValue) { foreach (Data x in xIENumberable) x.Age *= xValue; return xIENumberable; } // public static IEnumerable<Data> div(this IEnumerable<Data> xIENumberable, int xValue) { foreach (Data x in xIENumberable) x.Age /= xValue; return xIENumberable; } // } // class class Program { public class Classic { static public int add(int a, int b) { return a + b; } static public int sub(int a, int b) { return a - b; } static public int mul(int a, int b) { return a * b; } static public int div(int a, int b) { return a / b; } } // class public class Cascade1 { public int Result { get; private set; } public Cascade1(int x) { Result = x; } public Cascade1 add(int x) { Result += x; return this; } public Cascade1 sub(int x) { Result -= x; return this; } public Cascade1 mul(int x) { Result *= x; return this; } public Cascade1 div(int x) { Result /= x; return this; } } // class public class Cascade2 { public static void add(IEnumerable<Data> xIENumberable, int xValue) { foreach (Data x in xIENumberable) x.Age += xValue; } // public static void sub(IEnumerable<Data> xIENumberable, int xValue) { foreach (Data x in xIENumberable) x.Age -= xValue; } // public static void mul(IEnumerable<Data> xIENumberable, int xValue) { foreach (Data x in xIENumberable) x.Age *= xValue; } // public static void div(IEnumerable<Data> xIENumberable, int xValue) { foreach (Data x in xIENumberable) x.Age /= xValue; } // } // class static void Main(string[] args) { int r0 = 5; r0 = ((((r0 + 2) * 4) - 8) / 2) + 1; Console.WriteLine("Result is " + r0); // 11 int r1 = 5; r1 += 2; r1 *= 4; r1 -= 8; r1 /= 2; r1 += 1; Console.WriteLine("Result is " + r1); // 11 int r2 = 5; r2 = Classic.add(r2, 2); r2 = Classic.mul(r2, 4); r2 = Classic.sub(r2, 8); r2 = Classic.div(r2, 2); r2 = Classic.add(r2, 1); Console.WriteLine("Result is " + r2); // 11 int c1 = new Cascade1(5) .add(2) .mul(4) .sub(8) .div(2) .add(1) .Result; Console.WriteLine("Result is " + c1); // 11 List<Data> lList = new List<Data>(); for (int i = 0; i < 10; i++) lList.Add(new Data() { Age = i }); Cascade2.add(lList, 2); Cascade2.mul(lList, 4); Cascade2.sub(lList, 8); Cascade2.div(lList, 2); Cascade2.add(lList, 1); Console.Write("Array result is "); lList.ForEach(x => Console.Write(x.Age + " ")); Console.WriteLine(); lList.Clear(); for (int i = 0; i < 10; i++) lList.Add(new Data() { Age = i }); lList.add(2) .mul(4) .sub(8) .div(2) .add(1); Console.Write("Array result is "); lList.ForEach(x => Console.Write(x.Age + " ")); Console.WriteLine(); // using System.Linq; lList.Clear(); for (int i = 0; i < 50; i++) lList.Add(new Data() { Age = i }); List<Data> lResult = lList.Where(x => x.Age % 2 == 0).Take(10).ToList(); Console.Write("Array result is "); lResult.ForEach(x => Console.Write(x.Age + " ")); Console.ReadLine(); } // } // class } // namespace
example output:
Result is 11
Result is 11
Result is 11
Result is 11
Array result is 1 3 5 7 9 11 13 15 17 19
Array result is 1 3 5 7 9 11 13 15 17 19
Array result is 0 2 4 6 8 10 12 14 16 18
Posted on June 10, 2014, in Basic, C#, Programming Patterns, Structural Patterns and tagged C#, C-sharp, cascade, chaining, pattern, programming, Source code. Bookmark the permalink. 2 Comments.
Pingback: Builder Pattern | C# Hardcore Programming
Pingback: Code Construct