Introduction
This is a tutorial article that starts with an introduction to Fluent Interface Pattern in C#. This is followed by a discussion of the Hierarchical Fluent Interface. Finally, quite a complicated inheritance problem with Fluent Interface Pattern is discussed. The intended audience is intermediate-level C# programmers and above.
Fluent Interface Pattern
Fluent Interface Pattern is a design guideline for OO languages that advises that exposed API, that is, class public methods, should, in order to increase readability, try to “appear” as a domain-specific language (DSL). The main tool to create DSL is advised to be “method chaining”. We want to have an interface (API) like this:
Employee empl = new Employee();
empl.SetFirstName("John").SetLastName("Smith").SetAge(30).Print();
Method chaining is in C#, like in many OO languages, achieved by returning the object itself, that is, “this” object, as a method result. That enables the next method to be chained to the previous method’s return value. On an abstract level, we are really returning a domain context to a subsequent method in method chaining. Method chaining is terminated with a method that returns a void context.
A fluent Interface Pattern is just concerned with how the interface should look; it does not prescribe a pattern in a classical sense of how to achieve it. As long as you have an interface in the form of method chaining that “looks like” natural language and is therefore considered to be “user friendly”, it considers that you created your own form of “domain-specific language (DSL)” and is happy all about it.
Classic approach-Setters
Here is a classic code example using standard setters. This kind of programming is widely advised in OO and C# literature.
Code example
public class Employee
{
private string FirstName = null;
private string LastName = null;
private int Age = 0;
public void SetFirstName(string fName)
{
FirstName = fName;
}
public void SetLastName(string lName)
{
LastName = lName;
}
public void SetAge(int age)
{
Age = age;
}
public void Print()
{
string tmp = String.Format("FirstName:{0}; LastName:{1}; Age:{2}", FirstName, LastName, Age);
Console.WriteLine(tmp);
}
}
class Client
{
static void Main(string[] args)
{
Employee empl = new Employee();
empl.SetFirstName("John");
empl.SetLastName("Smith");
empl.SetAge(30);
empl.Print();
Console.ReadLine();
}
}
And here is the execution result:
FirstName:John; LastName:Smith; Age:30
Fluent Interface -Method Chaining
Here is a code example with method chaining. This is basically the same class as in the above example that follows the recommendations of the Fluent Interface Pattern. The main trick applied here is to return “this” object so methods can be chained.
Code example
public class Employee
{
private string FirstName = null;
private string LastName = null;
private int Age = 0;
public Employee SetFirstName(string fName)
{
FirstName = fName;
return this;
}
public Employee SetLastName(string lName)
{
LastName = lName;
return this;
}
public Employee SetAge(int age)
{
Age = age;
return this;
}
public void Print()
{
string tmp = String.Format("FirstName:{0}; LastName:{1}; Age:{2}", FirstName, LastName, Age);
Console.WriteLine(tmp);
}
}
class Client
{
static void Main(string[] args)
{
Employee empl = new Employee();
empl.SetFirstName("John").SetLastName("Smith").SetAge(30).Print();
Console.ReadLine();
}
}
And here is the execution result:
FirstName:John; LastName:Smith; Age:30
Fluent Interface -Method chaining using Extensions
Of course, we can use Extension methods in C# to achieve the same result as above. Please note that we needed to change the access level of attributes of employees to the public so that extension methods can use them.
Code example
public class Employee
{
public string FirstName = null;
public string LastName = null;
public int Age = 0;
}
public static class EmployeeExtensions
{
public static Employee SetFirstName(this Employee emp, string fName)
{
emp.FirstName = fName;
return emp;
}
public static Employee SetLastName(this Employee emp, string lName)
{
emp.LastName = lName;
return emp;
}
public static Employee SetAge(this Employee emp, int age)
{
emp.Age = age;
return emp;
}
public static void Print(this Employee emp)
{
string tmp = String.Format("FirstName:{0}; LastName:{1}; Age:{2}", emp.FirstName, emp.LastName, emp.Age);
Console.WriteLine(tmp);
}
}
class Client
{
static void Main(string[] args)
{
Employee empl = new Employee();
empl.SetFirstName("John").SetLastName("Smith").SetAge(30).Print();
Console.ReadLine();
}
}
And here is the execution result:
FirstName:John; LastName:Smith; Age:30
Hierarchical Fluent Interface
As mentioned before, the Fluent Interface pattern leaves it to the programmer and his judgment to decide how exactly his specific version of “domain-specific language (DSL)” is going to look. The programmer is free to choose method names and organization of method chaining that, to him, looks the most “user-friendly” and like natural language.
One typical use case is the creation of a Hierarchical Fluent Interface. In such a case, the programmer decides to group properties into hierarchical groups and, in that way, expose it to the library/class user. For example, a programmer might decide to divide Employee data into 2 groups, 1)PersonalData; and 2)EmploymentData. That would result in an interface like this:
Employee empl = new Employee();
empl.Fluent
.PersonalData
.FirstName("John").LastName("Smith").Age(30)
.EmploymentData
.Company("CNN").Position("Host").Salary(50000);
We will provide here a sample project on how to create such an interface. It is not necessarily the pattern programmer should follow to achieve Hierarchical Fluent Interface. The programmer is free to choose his own implementation technique if he desires. However, this gives a good and reusable example code. We will not be going into the design/implementation details of this project since most are self-explanatory.
Here is the class diagram:
And here is the code for Hierarchical Fluent Interface:
public class Employee
{
//PeronalData
public string FirstName = null;
public string LastName = null;
public int Age = 0;
//EmploymentData
public string Company = null;
public string Position = null;
public int Salary = 0;
public FluentEmployee Fluent
{
get { return new FluentEmployee(this); }
}
public override string ToString()
{
string tmp = String.Format("FirstName:{0}; LastName:{1}; Age:{2} \nCompany:{3}; Position:{4}; Salary:{5}",
this.FirstName, this.LastName, this.Age, this.Company, this.Position, this.Salary);
return tmp;
}
}
public class FluentEmployee
{
protected Employee _employee = null;
public FluentEmployee()
{
_employee = new Employee();
}
public FluentEmployee(Employee emp)
{
_employee = emp;
}
public FluentEmployeePersonalData PersonalData
{
get { return new FluentEmployeePersonalData(_employee); }
}
public FluentEmployeeEmploymentData EmploymentData
{
get { return new FluentEmployeeEmploymentData(_employee); }
}
}
public class FluentEmployeeEmploymentData : FluentEmployee
{
public FluentEmployeeEmploymentData(Employee emp)
: base(emp)
{
}
public FluentEmployeeEmploymentData Company(string comp)
{
_employee.Company = comp;
return this;
}
public FluentEmployeeEmploymentData Position(string pos)
{
_employee.Position = pos;
return this;
}
public FluentEmployeeEmploymentData Salary(int sal)
{
_employee.Salary = sal;
return this;
}
}
public class FluentEmployeePersonalData : FluentEmployee
{
public FluentEmployeePersonalData(Employee emp)
: base(emp)
{
}
public FluentEmployeePersonalData FirstName(string fName)
{
_employee.FirstName = fName;
return this;
}
public FluentEmployeePersonalData LastName(string lName)
{
_employee.LastName = lName;
return this;
}
public FluentEmployeePersonalData Age(int age)
{
_employee.Age = age;
return this;
}
}
class Client
{
static void Main(string[] args)
{
Employee empl = new Employee();
empl.Fluent
.PersonalData
.FirstName("John").LastName("Smith").Age(30)
.EmploymentData
.Company("CNN").Position("Host").Salary(50000);
Console.WriteLine(empl.ToString());
Console.ReadLine();
}
}
Here is the result of the execution:
FirstName:John; LastName:Smith; Age:30
Company:CNN; Position:Host; Salary:50000
Inheriting Fluent Interface classes
The problem arises when we want to inherit a class that implements Fluent Interface. Let us assume we have a class Employee and class Manager that inherits from it. Let it be like on this class diagram.
And here is the code:
public class Employee
{
protected string FirstName = null;
protected string LastName = null;
protected int Age = 0;
public Employee SetFirstName(string fName)
{
FirstName = fName;
return this;
}
public Employee SetLastName(string lName)
{
LastName = lName;
return this;
}
public Employee SetAge(int age)
{
Age = age;
return this;
}
public override string ToString()
{
string tmp = String.Format("FirstName:{0}; LastName:{1}; Age:{2}",
FirstName, LastName, Age);
return tmp;
}
}
public class Manager : Employee
{
protected int Bonus = 0;
public Manager SetBonus(int bonus)
{
Bonus = bonus;
return this;
}
public override string ToString()
{
string tmp = String.Format("FirstName:{0}; LastName:{1}; Age:{2}; Bonus:{3}",
FirstName, LastName, Age, Bonus);
return tmp;
}
}
What we would like to have is an interface like this:
Manager mgr = new Manager();
mgr.SetFirstName("John").SetLastName("Smith")
.SetAge(30).SetBonus(1000); //will not compile
But this will not compile. The reason is that we now have 2 types of “this” objects returned, of type Employee and of type Manager. Once we get back base class object “this” as Employee, the methods of class Manager and not available anymore.
So, method Employee.SetAge() would return an object of type Employee, and there is no way to method-chain methods from class Manager.
So, is there a way to trick the method Employee.SetAge() to return an object of type subclass Manager? We need that to be able to do our Fluent Interface, which is Method-Chaining.
The answer is yes; it is possible but technically complicated. It involves the usage of “Recursive Generics”. I am going to show here a working code example.
I am not going to in detail explain how the solution works. If you can read C# Generics code, you will be able to understand this quite advanced design.
The key problem that needs to be solved in this solution is how to communicate the return class type from the derived class to the base class. The key trick is to always return to the Fluent Interface object of the original (derived) class. That is resolved using Generics and type parameter SELF. Follow SELF from most derived classes (like CEO, Manager, Employee) down the inheritance hierarchy, and you will understand how this code works. Where clause (like “where SELF: EmployeeFluent<SELF>”) is there just to ensure that class is used as part of the inheritance hierarchy.
Here is the class diagram of this solution for Inheriting Fluent Interface classes.
Here is the code of this solution for Inheriting Fluent Interface classes.
public class EmployeeFluent<SELF>
where SELF : EmployeeFluent<SELF>
{
protected string FirstName = null;
protected string LastName = null;
protected int Age = 0;
public SELF SetFirstName(string fName)
{
FirstName = fName;
return (SELF)this;
}
public SELF SetLastName(string lName)
{
LastName = lName;
return (SELF)this;
}
public SELF SetAge(int age)
{
Age = age;
return (SELF)this;
}
public override string ToString()
{
string tmp = String.Format("FirstName:{0}; LastName:{1}; Age:{2}",
FirstName, LastName, Age);
return tmp;
}
}
public class Employee : EmployeeFluent<Employee> { };
public class ManagerFluent<SELF> : EmployeeFluent<SELF>
where SELF : ManagerFluent<SELF>
{
protected int Bonus = 0;
public SELF SetBonus(int bonus)
{
Bonus = bonus;
return (SELF)this;
}
public override string ToString()
{
string tmp = String.Format("FirstName:{0}; LastName:{1}; Age:{2}; Bonus:{3}",
FirstName, LastName, Age, Bonus);
return tmp;
}
}
public class Manager : ManagerFluent<Manager> { };
public class CEOFluent<SELF> : ManagerFluent<SELF>
where SELF : CEOFluent<SELF>
{
protected int CompanyShares = 0;
public SELF SetCompanyShares(int shares)
{
CompanyShares = shares;
return (SELF)this;
}
public override string ToString()
{
string tmp = String.Format("FirstName:{0}; LastName:{1}; Age:{2}; Bonus:{3}; CompanyShares:{4}",
FirstName, LastName, Age, Bonus, CompanyShares);
return tmp;
}
}
public class CEO : CEOFluent<CEO> { };
class Client
{
static void Main(string[] args)
{
CEO ceo1 = new CEO();
ceo1.SetFirstName("John").SetLastName("Smith")
.SetAge(30).SetBonus(1000).SetCompanyShares(5000);
Manager mgr = new Manager();
mgr.SetFirstName("Cedomir").SetLastName("Jokic")
.SetAge(40).SetBonus(2000);
Employee emp = new Employee();
emp.SetFirstName("Novak").SetLastName("Djokovic")
.SetAge(20);
Console.WriteLine(ceo1.ToString());
Console.WriteLine(mgr.ToString());
Console.WriteLine(emp.ToString());
Console.ReadLine();
}
}
Here is a sample execution of this solution for Inheriting Fluent Interface classes.
Conclusion
Fluent Interface Pattern has gained a lot of popularity and is here to stay. Just to mention, LINQ where is extensively used.