Click here to Skip to main content
15,886,797 members
Articles / Programming Languages / C#

Practical Applications of the Adaptive Interface Pattern: The Fluent Builder Context

Rate me:
Please Sign up or sign in to vote.
4.75/5 (4 votes)
5 Nov 2012Ms-PL2 min read 12.6K   10  
Fluent builder context

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:

C#
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.

C#
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:

C#
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.

C#
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:

C#
public class Car
{
    private Car() { }
    static public ICarBuildContext BeginBuild()
    {
        return new Car();
    }
    // ...
}

Usage:

C#
var myCar = Car.BeginBuild()
               .WithEngine(new V8Engine())
               .WithDriversSeat(new RacingSeat())
               // No passenger seat - this is a race car!
               .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.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)



Comments and Discussions

 
-- There are no messages in this forum --