So, the other week I took a look at the Factory Method and Abstract Factory design patterns. This week I'm continuing with the creational design patterns, this time focusing on the builder pattern!
The builder pattern is used when there is complex set up involved in creating an object. Like the other creational patterns, it also separates out the construction of an object from the object's use. The idea is that the builder allows you to do more complex set up for which a constructor would not be adequate. One example of this is if there is a set order in which set up steps need to happen. This can be achieved via an abstract base class which defines the order in terms of a set of steps. The individual steps are then overwritten for each type of the object being built.
In-keeping with the dinosaur theme, I thought we'd build some nice meals for our dinosaurs.
Here is our meal class:
It defines properties for the starter and main, and a field to hold the dessert. The
Dessert class looks like this:
ProduceToppingString method just formats the list of toppings as a string, and is used here to simplify the example code.
Meal class also provides a
ServeMeal method used to print out the meal to the console.
Now, we could just initialise a
Meal as follows:
However, as we can see here, our herbivore doesn't have any toppings on her meal! This is a pretty simple example, and there are only 4 things to remember to do, but you can see how it would be easy to forget steps in the process. In order to produce a meal in a consistent state, all of these properties need to be initialised. And this must also be done in the correct order, can't have the dessert being ready before the starter! (And crucially, the
Dessert needs to be initialised before any toppings can be added!) The final issue with this, is that there is no way to reproduce a specific meal for say, a stegosaurus...
If we start with a meal builder:
This defined the order in which the steps will be taken, and abstract methods for each step in the process.
We then override the steps in a concrete implementation:
This sets the various properties on the meal field in the builder base class. When
BuildMeal is called on a builder, these steps will be run in order and then the Meal will be returned.
So, when we call the following code:
The following will be written out to the console:
If we then want to build a different kind of meal, e.g. one for our TRex, we would write another implementation of the
MealBuilder base class:
And if we instead use a
TRexMealBuilder in our example we would get the following output:
In this way, we have defined the order in which a
Meal object must be created. We have created a set of steps which can be overwritten for different sorts of
Meal. And finally, we have made sure that
Meal objects are always in a consistent state!
Now, this is pretty good, but it does mean that we end up with a complex class hierarchy in which we need one builder for each type of meal. In a world of dependency injection, and maximising extensibility, is there a better way?
When using dependency injection in ASP.NET Core you can pass your own delegates into the builder in order to build the types you are registering, exchanging inheritance for composition. What would this look like for our dinosaur meal? This allows you to compose the builder yourself, as part of the set up for the application, instead of forcing you to derive from a base class for each new scenario. This is extremely extensible and powerful, and reduces the need for large class hierarchies within your application.
Here's a link to the GitHub repository if you want to have a look round the solution, otherwise look out for my next blog on design patterns in C#!