Object Instantiation in C# Part IV The Builder Pattern

Part III. Builders

In Part I of this two-part article, we talked about basic instantiation and different ways constructors can work.  In Part II we looked at using factory methods.  In Part III we looked at abstract factories and in this article we'll be looking at how to implement a builder.

A Builder

The difference between the abstract factory and the builder is that the abstract factory is primarily concerned with what is being built and the builder is primarily concerned with how something is being built.

In this example, we'll be building two different kinds of cars, a station wagon and a race car.  The cars are instances of the same Car class with different parts.

public abstract class Tire {}
public abstract class Engine { }
public abstract class Frame { }

public class Car
{

    private Tire[] m_tires;
    private Engine m_engine;
    private Frame m_frame;

    public Tire[] Tires
    {
        get { return m_tires; }
        internal set { m_tires = value; }
    }

    public Engine Engine
    {
        get { return m_engine; }
        internal set { m_engine = value; }
    }

    public Frame Frame
    {
        get { return m_frame; }
        internal set { m_frame = value; }
    }
}

Here are the different definitions for each type of part:

internal class RacingTire : Tire { }
internal class SixCylinderEngine : Engine { }
internal class RacingFrame : Frame { }

internal class NormalTire : Tire { }
internal class FourCylinderEngine : Engine { }
internal class StationWagonFrame : Frame { }

Next we will define an abstract factory that will build an instance of a car but instead of focusing on the type of car being built, we'll focus on the parts so instead of calling it an abstract factory, we'll call it a builder.

Here is our abstract builder contract:

public abstract class CarBuilderBase
{
    public abstract Car Build();
}

And here is our concrete builder:

public class CarBuilder : CarBuilderBase
{

    public CarBuilder(CarBuilderBase strategy)
    {
        m_strategy = strategy;
    }

    private CarBuilderBase m_strategy;

    public CarBuilderBase Strategy
    {
        get { return m_strategy; }
        set { m_strategy = value; }
    }

    public override Car Build()
    {
        return m_strategy.Build();
    }
}

Now, we'll define our two builders which will be responsible for how a car is constructed:

internal class StationWagonBuilder : CarBuilderBase
{
    public override Car Build()
    {
        Car result = new Car();
        result.Engine = new FourCylinderEngine();
        result.Frame = new StationWagonFrame();
        result.Tires = new Tire[]
        {
            new NormalTire(),
            new NormalTire(),
            new NormalTire(),
            new NormalTire()
        };

        return result;
    }
}

internal class RacingCarBuilder: CarBuilderBase
{
   public override Car Build()
    {
        Car result = new Car();
        result.Engine = new SixCylinderEngine();
        result.Frame = new RacingFrame();
        result.Tires = new Tire[]
        {
            new RacingTire(),
            new RacingTire(),
            new RacingTire(),
            new RacingTire()
        };

        return result;
    }
}

So now, in our implementing code we instantiate objects just as we do with the abstract factory pattern.

CarBuilder autoFactory = new CarBuilder(new RacingCarBuilder());
Car result = autoFactory.Build();
autoFactory.Strategy = new StationWagonBuilder();
Car stationWagon = autoFactory.Build();

As you can see, once you have a basic understanding of an abstract factory from the previous article, it is not really too much different from the builder.

In the next, and final article we'll look at a way to avoid instantiation altogether.

Until next time,
Happy coding

Up Next
    Ebook Download
    View all
    Learn
    View all