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

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

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:

Fluent Builder Recursive Generics - Chain problem

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.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!