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.
How would one unit test that code effectively?
It took me a while to wrap my head around the generics, but overall i really enjoy this pattern! End product looks really great!
I’m wondering if it would be a good idea to extend builder with extension methods?
Maybe something like
.AddSoftwareDeveloper(name)
where extension method would use something like this behind the scenes:.SetName(name)
.AtPosition("Software Developer")
.WithSalary(3500)
A lot of possibilities with this pattern! Can’t wait to have fun with it! 😀
Thank you Danilo. Extension methods are great and I use them regulary. Just write a code that suits your project the best, making it readable and easy to maintain. Have fun 😀
I’m having some trouble doing this nested, for example:
public abstract class Employee with T : Employee
{
public string Name { get; set; }
public string Position { get; set; }
public double Salary { get; set; }
public abstract string ToString();
public abstract class Builder
{
public abstract Builder WithSalary(double salary);
}
}
public abstract class Manager : Employee with T : Employee
{
public string Name { get; set; }
public string Position { get; set; }
public double Salary { get; set; }
public abstract string ToString();
public abstract class Builder
{
public abstract Builder WithSalary(double salary);
}
}
Can’t quite get my head around it.
The article is awesome and Fluent Builder looks great but I can’t see the difference (pros/con) between Fluent Builder and Setters / constructor params.
If the object has public setters like in the example above, why not use the object initializer syntax? It’s just as descriptive without an extra layer of abstraction and less code.
Hello JeffreyK. Thank you for your reading this article and your comment as well. This article is about Fluent interfaces and inheritance between them (as stated at the top of this article), and we have used a simply object just to show the point. If the object initializer syntax were used, we would lose the fluent interface. I understand what is your suggestion, but look at this from the fluent builder perspective. Thank you one more time for the comment and all the best.
One of the alternatives would be to decorate the top implementation with an interface and explicitly implement it:
public interface IEmployeeBuilder
{
IEmployeeBuilder SetName(string name);
IEmployeeBuilder AtPosition(string position);
IEmployeeBuilder WithSalary(double salary);
Employee Build();
}
public class EmployeeSalaryBuilder : EmployeePositionBuilder, IEmployeeBuilder
{
IEmployeeBuilder IEmployeeBuilder.SetName(string name)
{
SetName(name);
return this;
}
IEmployeeBuilder IEmployeeBuilder.AtPosition(string position)
{
AtPosition(position);
return this;
}
public IEmployeeBuilder WithSalary(double salary)
{
employee.Salary = salary;
return this;
}
}
Hello Kiryl. It is one of the alternatives and it is a good one as well. Thank you for that. All the best.