Easy Object Construction: Mastering the Builder Pattern in C#

Iede Snoek
November 25, 2023 0 Comment

Introduction

The builder pattern is a creational design pattern, i.e. it is a pattern for creating or instantiang objects of classes. We can use it for breaking down the construction process, into smaller, more manageable and testable steps.

In C# we can use classes and interfaces to implement this pattern.

What does it look like?

The basic builder pattern looks like this:

Let’s break this down into its parts:

  1. The Director. This is the client class for the Builder, and it wants to build some product.
  2. The Builder interface. This is the generic interface for any Builder, and it contains the methods to build a Product.
  3. The ConcreteBuilder. This is the class where we build the Product. Because we only use an interface, ConcreteBuilders you can swap ConcreteBuilders in and out to build different products.
  4. The product we want to build is in the Product class. This could also be an interface, or be a reference to one of its superclasses.

Implementing the builder pattern in C#

We will now implement a simple example of using the builder pattern in C#. In this example we will model a factory building different kind of bicycle, and to keep things simple we will just concentrate on two: an ATB Bike and a street bike.

We define a bike by its type name, and its number of wheels:

class Bicycle {
    public int NumberOfWheels { get; set; }
    public string BikeType { get; set; }

    public Bicycle()
    {
        this.NumberOfWheels = 0;
        this.BikeType = string.Empty;
    }

    public override string ToString()
    {
        return $"Type: {this.BikeType} and number of wheels: {this.NumberOfWheels}";
    }
}

We now define the BicycleBuilder interface. Classes implementing this interface just have to set the type and the number of wheels:

interface BicycleBuilder
{
    BicycleBuilder AddWheels();
    BicycleBuilder SetType();
    Bicycle GetBicycle();
}

Now it is time to implement a class for building the ATB-bikes:

class ATBBuilder:BicycleBuilder
{
    private Bicycle vehicle;

    public ATBBuilder()
    {
        this.vehicle = new Bicycle();
    }

    public BicycleBuilder AddWheels()
    {
        this.vehicle.NumberOfWheels = 2;
        return this;
    }

    public BicycleBuilder SetType()
    {
        this.vehicle.BikeType = "ATB";
        return this;
    }

    public Bicycle GetBicycle()
    {
        return this.vehicle;
    }
}

As you can see, we build the bike to strict specifications, with two wheels, and typename ‘ATB’.

The StreetBikeBuilder is built along the same lines:

class StreetBikeBuilder : BicycleBuilder
{
    private Bicycle vehicle;

    public StreetBikeBuilder()
    {
        this.vehicle=new Bicycle();
    }

    public BicycleBuilder AddWheels()
    {
        this.vehicle.NumberOfWheels = 3;
        return this;
    }

    public BicycleBuilder SetType()
    {
        this.vehicle.BikeType = "Street";
        return this;
    }

    public Bicycle GetBicycle()
    {
        return this.vehicle;
    }
}

Now all we need is a bike engineer to put all the parts together. Note that this engineer works with one builder-class at a time. Also note that since for example the AddWheels() method returns a BicycleBuilder the method class can be chained:

class BikeEngineer
{
    private BicycleBuilder builder;

    public BikeEngineer(BicycleBuilder builder)
    {
        this.builder = builder;
    }

    public Bicycle ConstructBike()
    {
        return builder.AddWheels().SetType().GetBicycle();
    }
}

Testing time

Now we can test this simple setup:

var builder = new StreetBikeBuilder();
var engineer = new BikeEngineer(builder);
var bike = engineer.ConstructBike();
Console.WriteLine(bike);

The code is quite easy to read, yet flexible:

  1. We define a bikebuilder, and pass it to the engineer
  2. The engineer then builds our bike
  3. We print out the specifications

Conclusion

The builder pattern is great if you want to hide the construction of certain classes. Of course, you can use constructors, but then then the responsibility for the construction of the class would lie with the client, and that is something you not always want.

As you can see the implementation in C# is easy to read, flexible and extendible. One thing that could done as an enhancements is to make the building-process thread-safe.