Introduction
So it’s been a little while since my last blog post due to the fact that I have recently moved to London to start a new job. If you’re involved in the .NET community in London, then look out for me at various conferences and user groups in the new year!
Following on from my previous post which explained the concept of an object who’s publicly exposed interface adapted based on its state, in this post I’ll explain how the technique can be used to create an elegant implementation of the Builder Pattern with a fluent interface.
For the example, we’ll take a simple immutable object which represents a Car
:
public class Car
{
public EngineBase Engine { get; private set; }
public SeatBase DriversSeat { get; private set; }
public SeatBase PassengerSeat { get; private set; }
public WheelType Wheels { get; private set; }
public BreakeBase Brakes { get; private set; }
}
In order to construct a new Car
object, we would have to introduce a constructor which includes all of these parameters. This could quickly lead to a huge constructor, potentially with a dozen or more parameters. Constructors like this are often very difficult to work with (I personally try to avoid any more than 3 parameters on any method/constructor). In these situations, we may wish to abstract the complex construction logic into a separate ‘Factory
’ class which tames the beastly constructor for us, but there is another way – The Fluent Build Context.
public interface ICarBuildContext
{
ICarBuildContext WithEngine(EngineBase engine);
ICarBuildContext WithDriversSeat(SeatBase seat);
ICarBuildContext WithPassengerSeat(SeatBase seat);
ICarBuildContext WithWheelType(WheelType wheelType);
ICarBuildContext WithBrakes(BrakeBase breakes);
Car Build();
}
This interface allows us to construct a Car
using a very nice and readable method chain:
var myCar = carBuilder.WithEngine(new DieselEngine())
.WithDriversSeat(new LuxuryHeatedSeat())
.WithPassengerSeat(new BucketSeat())
.WithWheelType(WheelType.Alloy)
.WithBrakes(new CeramicBrakes())
.Build();
But how do we integrate this interface with our Car
class? Through an often under-utilized feature of C# – Explicit Interface Implementation.
public class Car : ICarBuildContext
{
public EngineBase Engine { get; private set; }
public SeatBase DriversSeat { get; private set; }
public SeatBase PassengerSeat { get; private set; }
public WheelType Wheels { get; private set; }
public BreakeBase Brakes { get; private set; }
ICarBuildContext ICarBuildContext.WithEngine(EngineBase engine)
{
this.Engine = engine;
return this;
}
ICarBuildContext ICarBuildContext.WithDriversSeat(SeatBase seat)
{
this.DriversSeat = seat;
return this;
}
ICarBuildContext ICarBuildContext.WithPassengerSeat(SeatBase seat)
{
this.PassengerSeat = seat;
return this;
}
ICarBuildContext ICarBuildContext.WithWheelType(WheelType wheelType)
{
this.Wheels = wheelType;
}
ICarBuildContext ICarBuildContext.WithBrakes(BrakeBase brakes)
{
this.Brakes = brakes;
return this;
}
Car ICarBuildContext.Build()
{
return this;
}
}
By having Car
explicitly implement the build context interface itself, we are able to set the private
properties on the class – effectively allowing consumers to tunnel through the immutability of the class, but only on our terms. Once the Car
has been constructed, it becomes immutable again – the interface adapts by context.
The final step in the chain is to suppress the default constructor for Car
and introduce a static
factory method:
public class Car
{
private Car() { }
static public ICarBuildContext BeginBuild()
{
return new Car();
}
}
Usage:
var myCar = Car.BeginBuild()
.WithEngine(new V8Engine())
.WithDriversSeat(new RacingSeat())
.WithWheelType(WheelType.Alloy)
.WithBrakes(new CeramicBrakes())
.Build();
In a future blog post, I’ll go into detail about how this approach also gives us the ability to build in detailed construction validation into the process. I’ll also go into detail about how we can nest build contexts to build up a complex object graph in one method chain.