Builder Design Pattern
Builder Design Pattern
In today’s episode of Software Design Patterns you will learn everything about the Builder Design Pattern. The Builder Pattern is also pretty common and widely used. Different from the already covered behavioral pattern Observer, the Builder Pattern is a creational design patterns like Singleton and Factory Method pattern.
All the examples, source code and unit tests are in this GitHub repository.
What’s the purpose of the Builder Design Pattern?
The Gang-of-Four [1] reference book (“Design Patterns. Elements of Reusable Object-Oriented Software“) describes the intent of the Builder Pattern as the following.
Separate the construction of a complex object from its representation so that the same construction process can create different representations.
Basically, you have a builder and a director. In my example on GitHub I use different types of houses. There is a builder for flat and hipped houses. Those builders are created and passed to the director. The director itself constructs the different houses. To retrieve the built house the actual builder has a method for this (GetHouse()). Via this you can use the concrete house implementation.
How does the Builder look like?
In the picture below you find the Unified Modeling Language (UML) diagram.
In the linked GitHub repository you will find interfaces for the IHouseBuilder and the IDirector. Many resources advise you to use abstract base classes for the director and the builder - which is totally fine. I simply like interfaces more, so I went this route. The IHousebuilder interface contains the definition for building the house and retrieving the build instance. The IDirector simply has a construct method. In this method all steps should be done to build a specific thing - in our case a given type of house. The House class stores the model of a house. To make it simple I minimized the house to four property with getter only. To set value, you have to use the constructor when creating a house.
public interface IHouseBuilder
{
void BuildHouse();
House GetHouse();
}
public interface IDirector
{
void Construct();
}
public class House
{
public string PaintingColor { get; }
public string RoofType { get; }
public int NumbersOfFloors { get; }
public bool HasCellar { get; }
public House(string paintingColor, string roofType, int numbersOfFloors, bool hasCellar)
{
PaintingColor = paintingColor;
RoofType = roofType;
NumbersOfFloors = numbersOfFloors;
HasCellar = hasCellar;
}
}
The concrete house builders sets the correspondending values when creating a new instance.
public class FlatRoofHouseBuilder : IHouseBuilder
{
private House _house = default(House);
public void BuildHouse()
{
_house = new House("white", "flat", 1, false);
}
public House GetHouse()
{
return _house;
}
}
public class HippedRoofHouseBuilder : IHouseBuilder
{
private House _house = default(House);
public void BuildHouse()
{
_house = new House("gray", "hipped", 2, true);
}
public House GetHouse()
{
return _house;
}
}
The construct method on the director initiates the build process for the given house builder.
public class HouseBuildDirector : IDirector
{
private readonly IHouseBuilder _houseBuilder;
public HouseBuildDirector(IHouseBuilder houseBuilder)
{
_houseBuilder = houseBuilder;
}
public void Construct()
{
_houseBuilder.BuildHouse();
}
}
In the unit tests you see the whole process of how to use the builder pattern. In this case it seems pretty odd to use such a pattern, but imagine that the complete build/construct process takes many complex steps.
public class BuilderPatternTest
{
[Fact]
public void ShouldBuildFlatHouse()
{
var flatHouseBuilder = new FlatRoofHouseBuilder();
var houseBuildDirector = new HouseBuildDirector(flatHouseBuilder);
houseBuildDirector.Construct();
var house = flatHouseBuilder.GetHouse();
house.HasCellar.Should().BeFalse();
house.PaintingColor.Should().Be("white");
house.RoofType.Should().Be("flat");
house.NumbersOfFloors.Should().Be(1);
}
[Fact]
public void ShouldBuildHippedHouse()
{
var hippedRoofHouseBuilder = new HippedRoofHouseBuilder();
var houseBuildDirector = new HouseBuildDirector(hippedRoofHouseBuilder);
houseBuildDirector.Construct();
var house = hippedRoofHouseBuilder.GetHouse();
house.HasCellar.Should().BeTrue();
house.PaintingColor.Should().Be("gray");
house.RoofType.Should().Be("hipped");
house.NumbersOfFloors.Should().Be(2);
}
}
Appendix: There are many ways of describing the builder pattern. Often it's mistaken by a fluent library where you chain different methods and eventually build an object from it. Let me know in the comments below how you find the builder or how you learned it.
If you have any questions or comments feel free to leave them below.
That’s it. Happy coding as always :).
References
[1] –Design patterns, software engineering, object-oriented programming