In the previous post, we have been talking about Builder and the Fluent Builder design patterns. So, we strongly recommend reading that one before you continue with this post if you are not familiar with the Fluent Builder pattern. In this post, we are going to get a Fluent Builder to a higher level and show how we can use generics while inheriting from another Fluent Builder.
When builders inherit from other builders, nothing particular is going to happen and everything should remain the same. But if one Fluent Builder inherits from another one, well, we are going to have a problem with chaining actions. Therefore, we are going to use a Recursive Generics approach to enable the default behavior of our fluent interfaces.
You can download the source code from here: Fluent Builder With Recursive Generics – Source Code
For the complete list of articles from this series check out C# Design Patterns.
The Problem With the Fluent Builder Inheritance
Let’s imagine that we want to build an Employee
object. So obviously, the first thing to do is to create our model class:
public class Employee { public string Name { get; set; } public string Position { get; set; } public double Salary { get; set; } public override string ToString() { return $"Name: {Name}, Position: {Position}, Salary: {Salary}"; } }
To continue on, we are going to create a builder class to build the Name
part of our object:
public class EmployeeInfoBuilder { protected Employee employee = new Employee(); public EmployeeInfoBuilder SetName(string name) { employee.Name = name; return this; } }
Now, we can create another builder class to build the Position
part, and that class is going to inherit from the EmployeeInfoBuilder
class because we want to reuse our employee object:
public class EmployeePositionBuilder: EmployeeInfoBuilder { public EmployeePositionBuilder AtPosition(string position) { employee.Position = position; return this; } }
Finally, we can start making calls towards this builder classes:
But, as we can see, we are not able to create a required object. This is because the SetName
method will return an instance of type EmployeeInfoBuilder
which currently doesn’t implement or inherit the AtPosition
method. This is quite okay since the EmployeeInfoBuilder
class is a higher order class and the EmployeePositionBuilder
class inherits from it, and not another way around.
So, we need to find a solution to propagate information from the derived class to the base class. And the solution is in recursive generics approach.
Implementing Recursive Generics with Fluent Builder
So, let’s start with the EmployeeBuilder
abstract class, which will be responsible for instantiating and providing our employee object:
public abstract class EmployeeBuilder { protected Employee employee; public EmployeeBuilder() { employee = new Employee(); } public Employee Build() => employee; }
Once we are done, we can continue to the EmployeeInfoBuilder
modification. We have seen that the SetName
can’t return the EmployeeInfoBuilder
type, it needs to return a generic type. Having this in mind, let’s modify our class:
public class EmployeeInfoBuilder<T>: EmployeeBuilder where T: EmployeeInfoBuilder<T> { public T SetName(string name) { employee.Name = name; return (T)this; } }
Ok, what???
Well, it is not that complicated as it may look like at a first glance.
We’ve said that the SetName
method needs to return a generic type, therefore our class is generic as well. It needs to inherit from the EmployeeBuilder
class because we need that employee object. Finally, we must make sure to get the right type for the T type in our class. We can achieve this by restricting our T type to the EmployeeInfoBuilder
type.
Now we can continue to the EmployeePositionBuilder
modification:
public class EmployeePositionBuilder<T>: EmployeeInfoBuilder<EmployeePositionBuilder<T>> where T: EmployeePositionBuilder<T> { public T AtPosition(string position) { employee.Position = position; return (T)this; } }
By doing this we enabled inheritance in both of these classes. They support the fluent builder interface approach and they can return the required type now.
This is very useful in our case because our employee needs his salary. We can easily add the salary now by using the WithSalary
method of the EmployeeSalaryBuilder
class:
public class EmployeeSalaryBuilder<T>: EmployeePositionBuilder<EmployeeSalaryBuilder<T>> where T: EmployeeSalaryBuilder<T> { public T WithSalary(double salary) { employee.Salary = salary; return (T)this; } }
At this moment, we know how to create Builder classes with recursive generics.
But we can’t start building our object yet.
That’s because it is not entirely clear which type we should when instantiating the EmployeeInfoBuilder
class.
Therefore, we are going to create an API to allow the building of our object:
public class EmployeeBuilderDirector : EmployeeSalaryBuilder<EmployeeBuilderDirector> { public static EmployeeBuilderDirector NewEmployee => new EmployeeBuilderDirector() }
Now we can start building our object in a fluent way:
class Program { static void Main(string[] args) { var emp = EmployeeBuilderDirector .NewEmployee .SetName("Maks") .AtPosition("Software Developer") .WithSalary(3500) .Build(); Console.WriteLine(emp); } }
Awesome.
Now we know how to enable fluent interface inheritance by using a recursive generics approach.
Conclusion
In the next article, which is going to be related to the Builder pattern again, we are going to talk about Faceted Builder and show you how to use a facade to create an object which requires more than one builder class.