top of page

Fluent Builder Pattern — C#



It’s very common to have complex objects in our solutions. Objects that have multiple fields and each field is difficult to build. Who is familiar with this type of object? Everyone, I guess. Sometimes we have gigantic constructors to create an object. Let’s see an example:

public class Order
{
    public Buyer Buyer { get; set; }
    public Seller Seller { get; set; }
    public Product Product { get; set; }
    public double Price { get; set; }
    (...)    public Order(Buyer buyer, Seller seller, 
                 Product product, double price, (...))
    {
            Buyer = buyer;
            Seller = seller;
            Product = product;
            Price = price;
            (...)
    }
}

As you can see it will be a feast to create an Order . Each field has multiple fields and some of them could have nested objects. Good luck to create a new instance of this manually.


Why is a builder the solution?

This pattern tells us to extract the object construction code out of its own class and put it in a separate object which we call a builder. This is the class responsible to create an instance of a specific object. Increase the readability and the simplicity of your code. It is the one who knows how to create that object with all the rules attended.


The most important advantages are:

  • More maintainable code

  • More readable code

  • Reduce errors during the creation of an object

But be careful, I don’t advise you to use this pattern for all types of objects. A basic object doesn’t need so much effort to build it. You must check the advantages that you will get when applying this pattern. We want that you get time, not wasting it creating builder classes for all of your objects.


The OrderBuilder implementation

public class OrderBuilder
{
    private Order _order = new Order();
    private readonly IBuyerRepository _buyerRepository;
    private readonly ISellerRepository _sellerRepository;    private OrderBuilder(
                     IBuyerRepository buyerRepository, 
                     ISellerRepository sellerRepository)
    {
        _buyerRepository = buyerRepository;
        _sellerRepository = sellerRepository;
    }    public static OrderBuilder Init(
                               IBuyerRepository buyerRepository, 
                               ISellerRepository sellerRepository)
    {
        return new OrderBuilder(buyerRepository, sellerRepository);
    }    public Order Build() => _order;    public OrderBuilder SetBuyer(int buyerId)
    {
        _order.Buyer = _buyerRepository.GetById(buyerId);
        return this;
    }    public OrderBuilder SetSeller(int sellerId)
    {
        _order.Seller = _sellerRepository.GetById(sellerId);
        return this;
    }
    
    (...)
}

This is an example of a OrderBuilder . To keep the things even simpler we put the logic to get the complex objects from the builder side, as Buyer and Seller classes in the above code block. With this, we help who wants to create an Order as we just need the identifiers from each complex class. To allow this feature we need to add some repositories dependencies. The Init static method was created for this purpose. This method also allows us to use the builder without an explicit instantiation. However, if you prefer you could have a public constructor which receives the necessary repositories.


Usage without builder

Buyer buyer = _buyerRepository.GetById(buyerId);
Seller seller = _sellerRepository.GetById(sellerId);
Product product = _productRepository.GetById(productId);Order order = new Order(buyer, seller, product, price);


Usage with the builder with static initiation

Order order = OrderBuilder.Init(buyerRepository, sellerRepository)
                          .SetBuyer(buyerId)
                          .SetSeller(sellerId)
                          .Build();


Usage with the builder with a public constructor

OrderBuilder orderBuilder = new OrderBuilder(buyerRepository,
                                             sellerRepository);
Order order = orderBuilder.SetBuyer(buyerId)
                          .SetSeller(sellerId)
                          .Build();

I think that the differences are visible just looking at the above code examples. With the builder, we just need one line of code comparing to without the builder which we must get and build the required objects. In the other two examples, using the builder, I show you two different approaches. One of them you can initiate the builder without initialization of it using the static method Init. The other has the same behavior but you need to create a builder instance since it has a public constructor. The choice between these two options is just according to your taste for code.


What if I have multiple builders? How can I organize my code?

You could create an interface to be implemented by any builder.


The generic interface IBuilder just has a Build method to return the type that is defined by each class who implements it.


Constructors with optional parameters

Sometimes we need to have an object that supports multiple combinations. Let’s imagine a burger constructor:

public Burger(int numPatties, bool cheese, bool bacon, 
              bool pickles, bool letuce, bool tomato)
{ ... }


Creating a simple Fluent Builder

We can create a builder class with a fluent approach. We have a simple Burger class that defines a default number of patties.

public class Burger
{
    public int NumPatties { get; set; }
    public bool Cheese { get; set; }
    public bool Bacon { get; set; }
    public bool Pickles { get; set; }
    public bool Letuce { get; set; }
    public bool Tomato { get; set; }    public Burger(int numPatties = 1)
    {
        NumPatties = numPatties;
    }    public Burger(bool cheese, bool bacon, bool pickles, 
              bool letuce, bool tomato, int numPatties = 1)
    { ... }
}

One possible option is having a constructor with all boolean parameters to be passed with some value. The result will be something like the following example:

Burger uglyBurger = new Burger(true, true, false, false, false);

However, this is not a beautiful code to present. In those cases, we can create a builder class with multiple methods that are easy to understand their function once they are fluent.

public class BurgerBuilder
{
    private Burger _burger = new Burger();    public Burger Build() => _burger;    public BurgerBuilder WithPatties(int num)
    {
        _burger.NumPatties = num;
        return this;
    }
    public BurgerBuilder WithCheese()
    { 
        _burger.Cheese = true;
        return this;
    }
    public BurgerBuilder WithBacon()
    {
        _burger.Bacon = true;
        return this;
    }
    (...)
}

With this implementation we can have something like:

BurgerBuilder burgerBuilder = new BurgerBuilder();
Burger awesomeburger = burgerBuilder.WithCheese()
                                    .WithBacon()
                                    .Build();

The result? A delicious burger with cheese and bacon…so good and so simple to build it.


Conclusion

One of the most important things to keep in mind when you are thinking to use the builder pattern is to understand their advantages. As I explained earlier you gain quality in your code for some scenarios where you design some objects that are complex to build.

This pattern is a good option when you have an object which has a constructor with more than a few parameters and when those parameters are objects with nested classes. A constructor that has multiple optional parameters is also a good scenario to use this pattern.


Source: Medium


The Tech Platform

0 comments
bottom of page