Design patterns Ep.3 - Creational - Builder
Orestis Meikopoulos
Engineering Manager & Technical Business Development Lead | Cultivating Technical Leadership | C# & .NET Content Creator | Public Speaker
In the previous articles we explored details on the following patterns:
In today's article, we are going to examine another?creational?design pattern and in particular, the?Builder?pattern.
Problem
Let us understand the Builder Design Pattern with an example of a problem statement. Imagine we need to build a really complex object, requiring intensive step-by-step initialization of many fields and possible nested objects. Often, this type of initialization logic is gathered inside a really large constructor, which take as input many parameters.
For example, let's examine how we would construct a Car object. For the sake of simplicity let's say that in order to build a simple car object, we need to install a particular engine type, a number of comfortable seats and some doors in order to be able to enter it.
But what if, we would now want to have the ability to install various other features like, a GPS or a Trip Computer system?
The simplest solution that comes to mind, is to extend the base Car class and create a set of subclasses to cover all combinations of the newly introduced parameters. With this, you will eventually end up with a considerable amount of new subclasses (e.g. CarWithGPS, CarWithTripComputer, CarWithGPSAndTripComputer). Also, with any new parameter introduced (e.g. Voice Recognition system), you will need to extend the class hierarchy even more.
Another approach you can follow, in order to prevent creating many new subclasses for each parameter combination you want to support, is to create a very large constructor inside the base Car class containing all the possible parameters that control the Car object.
Using this approach, although you can eliminate the need for creating many new subclasses for every possible parameter combination, it introduces another problem. While in most cases, many of the parameters are going to be unused, the constructor calls are going to become pretty ugly. For example, only a fraction of cars will have GPS, Trip Computer and Voice Recognition systems installed, so those parameters will be useless most of the time.
Solution
We can fulfill the above requirements and solve the above problems by using the Builder Design Pattern, which is a creational design pattern that lets you?construct complex objects step by step. In other words, the builder pattern lets us extract the construction code of a complex object out of its class and move it to separate objects called builders, so that the same construction process can create different representations.
The pattern organizes object construction into a set of steps (SetSeats, SetEngineType, SetDoors). To create an object, you execute a series of steps on some builder object. The important part is that you don’t need to call all of the steps. You can only call those steps necessary for producing a particular representation of an object. Also, the Builder object doesn’t allow other objects to access the product while it’s being built.
Some of the construction steps might require different implementation when you need to build various representations of the product. In this case, you can create several different builder classes that implement the same set of building steps, but in a different manner. Then you can use these builders in the construction process (i.e., an ordered set of calls to the building steps) to produce different kinds of objects.
You can also extract a series of calls to the builder steps you use to construct a product into a separate class called director. The director class defines the order in which to execute the building steps, while the builder provides the implementation for those steps. Having a director class in your program is entirely optional. You can always call the building steps in a specific order directly from the client code, but having a director class gives you a good place to put various construction methods (e.g. ConstructSportsCar, ConstructSUVCar), so that you can reuse them inside your application. In addition, the director class completely hides the details of product construction from the client code. The client only needs to provide a builder object to the director, launch the construction process through calling a director's construction routine and finally get the result from the builder.
Let's examine some details of the pattern and see some code in C#.
Structure
Participants
领英推荐
You can define the methods of the IBuilder interface to either have a return type of void or a return type of the same interface (IBuilder), to achieve a method call chain approach in your client code. In our example we follow the second approach.
The output of the above code for the "ConstructSportsCar" method call of the director on both CarBuilder and CarManualBuilder objects would be:
On the other hand, if we changed our Main method's code and called the "ConstructSUV" method of the Director class, the output would be:
Applicability
Use the Builder pattern when:
1) The algorithm for creating a complex object should be independent of the parts that make up the object and how they're assembled.
2) The construction process must allow different representations for the object that's constructed.
You can find the source code for the above example?here.
That's all for today. Cheers!
Manager in Technology Consulting at EY
3 年Great job Orestis Meikopoulos !!Totally recommend it !