Get up to 80 % extra points for free! More info:

Method Chaining and Method Cascading

In the last lesson, Servant, we talked about the Servant design pattern. We use it if we want to add additional functionality to several classes without interfering with their interfaces.

In today's article, we're gonna explain the importance of auxiliary variables in source code and the Method Chaining design pattern with the fluent interface that the implementation of this pattern provides. We'll also explain Method Cascading and in which cases it replaces Method Chaining.

Motivation

Creating auxiliary variables isn't always necessary for the program to run, but it's important to keep the source code readable for the programmer. It's a common practice to create unnecessary variables that basically slow the program down, but allow people to read the source code. Sometimes they can also be counterproductive and it's better to get rid of them. Let's show examples of when it's worth using the auxiliary variables and when it's not.

Useful auxiliary variables

Let's start with a code that adds 2 numbers entered to the console as strings. It parses the numbers to the int type, sums and prints them. Let's create too many auxiliary variables first:

string input1 = Console.ReadLine();
string input2 = Console.ReadLine();
int a = int.Parse(input1);
int b = int.Parse(input2);
int c = a + b;
Console.WriteLine(c);

The input1 and input2 variables are basically unnecessary here, and we can simply get rid of them without decreasing the readability of the source code:

int a = int.Parse(Console.ReadLine());
int b = int.Parse(Console.ReadLine());
int c = a + b;
Console.WriteLine(c);

We nest the methods into another. The code is now optimal, we can read it simply from left to right. If we keep nesting the methods, we could end up with a one-line program completely without auxiliary variables:

Console.WriteLine(int.Parse(Console.ReadLine()) + int.Parse(Console.ReadLine()));

However, it's quite hard to read this code, and we certainly shouldn't write it this way. This isn't because there are too many methods on one line, but the methods are recursively nested, and we need to read them from the middle and remember the intermediate results.

Note: In case of performance problems, removing the auxiliary variables may sometimes pay off in certain parts of the application. However, we usually handle these situations no before they actually happen, according to the Premature optimization is the root of all evil principle.

Unnecessary auxiliary variables

Sometimes, auxiliary variables are doing more harm than good. These are the cases where we need to keep manipulating with the same object and keep referring to the variable with it or modifying its instance somehow. Let's show a code that takes the first 50 characters from a given string, removes white characters around the string and converts it to lowercase. As true rookies, we'd even create a separate variable for each intermediate result, which we won't do. However, we could think of using an auxiliary variable to store the current result:

string s = "Who is General Failure and why is he reading my hard disk?";
s = s.Substring(0, 50);
s = s.Trim(s);
s = s.ToLower();

Repeating of s = doesn't improve the code readability in any way and just takes up space. Any repetition in the source code is almost always a design mistake (see the DRY principle).

Other unnecessary variables

Variables can also mess up source codes when we set different properties of one object:

Button button = new Button();
button.Text = "Save";
button.Width = 100;
button.Height = 50;
button.Frame = 2;
button.Color = Color.Blue;
button.Icon = Icon.FloppyDisk;
button.Selected = true;
button.Shortcut = {"ctrl", "s"};

The code is unnecessarily long and confusing. You may have thought of putting all the parameters into the constructor, but that wouldn't make it very clear, because we'd forget which parameter is which. E.g. the value of 2 would be very confusing without the information that it indicates the frame next to it and assuming there are so many other values.

How to get out of it?

Method chaining

Method Chaining, sometimes referred to as Fluent Interface, is the technique of calling a method directly on the return value of another method without using auxiliary variables. This technique is only possible if the object on which we call the method supports this usage, meaning it exposes a fluent interface. Method Chaining is usually implemented by a method returning the same object instance on which it's been called (by returning this), or returning an object with the same interface. It's often used for strings, setters and collections. However, we can also use it on other objects types.

You have probably noticed that we can call methods like ToLower(), Substring(), Trim() and similar on a string instance, one after another, since they always return a string and are called on a string. At this point, you've used method chaining. Let's look at what the deterrent example above would look like if we called the methods one after another:

string s = "Who is General Failure and why is he reading my hard disk?";
s = s.Substring(0, 50).Trim(s).ToLower();

The result is very impressive and still perfectly readable as we call the methods from left to right.

Collections

Method Chaining is often used for working with collections. It doesn't even have to return the same data type, just the supported interfaces. In C# .NET, using Method Chaining, we can select all adult users from a collection, sort them by age, those with the same age by name and select their names. Thanks to the fluent interface, we can do it all on a single line:

var result = users.Where(u => u.Age >= 18).OrderBy(u => u.Age).ThenBy(u => u.Name).Select(u => u.Name);

In the example above, we take an advantage of the fact that each method is called on a data collection and its result is also a data collection. So we can call another method directly on the return value of the previous one. The resulting notation is very clear and looks similar to SQL language queries. If you are confused with the arrow operators (=>), that's C# syntax for lambda functions, anonymous methods by which we specify what we need from the given method.

Setters

And what about the example with instantiating a button and setting a large number of its parameters? We'll implement setters supporting the fluent interface to the class and set the parameters with them:

Button button = new Button();
button.setText("Save")
      .setWidth(100)
      .setHeight(50)
      .setFrame(2)
      .setColor(Color.Blue)
      .setIcon(Icon.FloppyDisk)
      .setSelected(true)
      .setShortcut({"ctrl", "s"});

The code is now much more readable and shorter. The methods in the Button class would look like as follows:

public Button setText(string text)
{
    this.Text = text;
    return this;
}

public Button setWidth(int width)
{
    this.Width = width;
    return this;
}

public Button setHeight(int height)
{
    this.Height = height;
    return this;
}

public Button setFrame(int frame)
{
    this.Frame = frame;
    return this;
}

// ...

The construction of the object, along with the setters, is often separated to an external object called Builder, which increases the code readability.

Method Cascading

Some programming languages ​​have Method Chaining built directly in their syntax, such as the Dart language. We then refer to this mechanism as Method Cascading. It's typically implemented through the .. (double dot) operator and allows us to access properties or call methods on an object without having to specify it again and again. Maybe some of you remember the with construction from old Pascal that did the similar thing.

var button = new Button()
    ..text("Save")
        ..width(100)
        ..height(50)
        ..frame(2)
        ..color(Color.Blue)
        ..icon(Icon.FloppyDisk)
        ..selected(true)
        ..shortcut(["ctrl", "s"]);

In the same way, we can also call methods, and therefore replace Method Chaining completely, if supported by our programming language. Most of them are unfortunately unable to do so, so most often we just use the fluent interface.


 

Previous article
Servant
All articles in this section
Design Patterns
Article has been written for you by David Capka Hartinger
Avatar
User rating:
No one has rated this quite yet, be the first one!
The author is a programmer, who likes web technologies and being the lead/chief article writer at ICT.social. He shares his knowledge with the community and is always looking to improve. He believes that anyone can do what they set their mind to.
Unicorn university David learned IT at the Unicorn University - a prestigious college providing education on IT and economics.
Activities