Skip to content
Carmel Eve By Carmel Eve Software Engineer I
Design patterns in C# - The Builder Pattern

So, the other week I took a look at the Factory Method and Abstract Factory design patterns. This week I'm continuing with the creational design patterns, this time focusing on the builder pattern!

The builder pattern is used when there is complex set up involved in creating an object. Like the other creational patterns, it also separates out the construction of an object from the object's use. The idea is that the builder allows you to do more complex set up for which a constructor would not be adequate. One example of this is if there is a set order in which set up steps need to happen. This can be achieved via an abstract base class which defines the order in terms of a set of steps. The individual steps are then overwritten for each type of the object being built.

In-keeping with the dinosaur theme, I thought we'd build some nice meals for our dinosaurs.

Here is our meal class:

public class Meal
{
    public string Starter { get; set; }

    public string MainCourse { get; set; }

    private Dessert Dessert;

    public void SetDessert(string dessertName)
    {
        Dessert = new Dessert(dessertName);
    }
   
    public void AddTopping(string topping)
    {
        Dessert.AddTopping(topping);
    }
    
    public void ServeMeal()
    {
        Console.WriteLine($"Bringing you {Starter}, " +
            $"then {MainCourse}, " +
            $"then {Dessert.Name} with {Dessert.ListToppings()} on top");
    }
}

It defines properties for the starter and main, and a field to hold the dessert. The Dessert class looks like this:

public class Dessert
{
    public string Name { get; set; }
    private readonly IList<string> toppings = new List<string>();

    public Dessert(string dessertName)
    {
        Name = dessertName;
    }

    public void AddTopping(string topping)
    {
        toppings.Add(topping);
    }

    public string ListToppings()
    {
        return ProduceToppingString(toppings);
    }
}

Here the ProduceToppingString method just formats the list of toppings as a string, and is used here to simplify the example code.

The Meal class also provides a ServeMeal method used to print out the meal to the console.

Now, we could just initialise a Meal as follows:

static void CreateAMealExample()
{
    var meal = new Meal();

    meal.Starter = "a few green leaves";
    meal.MainCourse = "a huge plate of ferns";
    meal.SetDessert("fruit and berries");

    meal.ServeMeal();
}

However, as we can see here, our herbivore doesn't have any toppings on her meal! This is a pretty simple example, and there are only 4 things to remember to do, but you can see how it would be easy to forget steps in the process. In order to produce a meal in a consistent state, all of these properties need to be initialised. And this must also be done in the correct order, can't have the dessert being ready before the starter! (And crucially, the Dessert needs to be initialised before any toppings can be added!) The final issue with this, is that there is no way to reproduce a specific meal for say, a stegosaurus...

If we start with a meal builder:

public abstract class MealBuilder
{
    protected Meal meal = new Meal();

    public Meal BuildMeal()
    {
        AddStarter();
        AddMain();
        AddDessert();
        AddToppings();

        return meal;
    }

    protected abstract void AddStarter();
    protected abstract void AddMain();
    protected abstract void AddDessert();
    protected abstract void AddToppings();
}

This defined the order in which the steps will be taken, and abstract methods for each step in the process.

We then override the steps in a concrete implementation:

public class StegosaurusMealBuilder : MealBuilder
{
    protected override void AddStarter()
    {
        this.meal.Starter = "a few green leaves";
    }

    protected override void AddMain()
    {
        this.meal.MainCourse = "a huge plate of ferns";
    }

    protected override void AddDessert()
    {
        this.meal.SetDessert("fruit and berries");
    }

    protected override void AddToppings()
    {
        this.meal.AddTopping("crunchy flowers");
    }
}

This sets the various properties on the meal field in the builder base class. When BuildMeal is called on a builder, these steps will be run in order and then the Meal will be returned.

Programming C# 10 Book, by Ian Griffiths, published by O'Reilly Media, is now available to buy.

So, when we call the following code:

public static void BuilderExample()
{
    MealBuilder builder = new StegosaurusMealBuilder();
    Meal meal = builder.BuildMeal();

    meal.ServeMeal();
}

The following will be written out to the console:

Output written in console

If we then want to build a different kind of meal, e.g. one for our TRex, we would write another implementation of the MealBuilder base class: And if we instead use a TRexMealBuilder in our example we would get the following output:

TRexMealBuilder output in the console

In this way, we have defined the order in which a Meal object must be created. We have created a set of steps which can be overwritten for different sorts of Meal. And finally, we have made sure that Meal objects are always in a consistent state!

The Introduction to Rx.NET 2nd Edition (2024) Book, by Ian Griffiths & Lee Campbell, is now available to download for FREE.

Now, this is pretty good, but it does mean that we end up with a complex class hierarchy in which we need one builder for each type of meal. In a world of dependency injection, and maximising extensibility, is there a better way?

When using dependency injection in ASP.NET Core you can pass your own delegates into the builder in order to build the types you are registering, exchanging inheritance for composition. What would this look like for our dinosaur meal? This allows you to compose the builder yourself, as part of the set up for the application, instead of forcing you to derive from a base class for each new scenario. This is extremely extensible and powerful, and reduces the need for large class hierarchies within your application.

Doodle of author feeding stegosaurus.

Here's a link to the GitHub repository if you want to have a look round the solution, otherwise look out for my next blog on design patterns in C#!

Carmel Eve

Software Engineer I

Carmel Eve

Carmel is a software engineer, LinkedIn Learning instructor and STEM ambassador.

Over the past four years she has been focused on delivering cloud-first solutions to a variety of problems. These have ranged from highly-performant serverless architectures, to web applications, to reporting and insight pipelines and data analytics engines.

In her time at endjin, she has written many blog posts covering a huge range of topics, including deconstructing Rx operators and mental well-being and managing remote working.

Carmel's first LinkedIn Learning course on how to prepare for the Az-204 exam - developing solutions for Microsoft Azure - was released in April 2021. Over the last couple of years she has also spoken at NDC, APISpecs and SQLBits. These talks covered a range of topics, from reactive big-data processing to secure Azure architectures.

She is also passionate about diversity and inclusivity in tech. She is a STEM ambassador in her local community and is taking part in a local mentorship scheme. Through this work she hopes to be a part of positive change in the industry.

Carmel won "Apprentice Engineer of the Year" at the Computing Rising Star Awards 2019.

Carmel worked at endjin from 2016 to 2021.