DEV Community

Theodore Karropoulos
Theodore Karropoulos

Posted on • Edited on

Builder Pattern

Builder is a creational design pattern that lets you construct objects step by step. Builder design pattern separates the construction of a complex object from its representation so that the same construction process can create different representations.

Problem

Imagine we have to instantiate an object that has quite a lot of arguments need to be passed to it before we are ready to initialize it. We can solve this issue by creating a constructor that takes a large number of arguments and then different setter methods to set the optional parameters. There is a problem with this approach though. First of all we have to create numerous constructors each one taking a different number of parameters, so we can instantiate the class with the correct combination of parameters on each situation (telescoping constructor anti-pattern). Another problem with this approach is that once our class has to much constructors and constructors are four or more parameters long it becomes difficult to remember the required order of the parameters as well as what particular constructor you might want in a given situation.

Solution

The Builder pattern solves this issue by providing a way to build the object step-by-step and provide a method that will return the final object.

Builder pattern consists of the following parts

  • Director, director is a class that decides in what steps should the complex class be built. Having a director class in your program isn’t strictly necessary. You can always call the building steps in a specific order directly from the client code
  • Builder Interface, defines the interface with all the necessary methods to build a Product class. These methods are common to all implementations of the Builder.
  • Concrete Builder Class, represents an implementation of the Builder interface.
  • Product, is a complicated class that you need to build. ### Implementation In our example we are going to build a car. Car is a complex object that can be constructed in a large number of different ways. Instead of passing all of our arguments in a huge constructor we are going to extract the car assembly code into a separate car builder class that contains a set of methods for configuring various parts of a car. Bellow is the sample code:
// This is our final product
public class Car
{
    public string Engine { get; set; }
    public int Seats { get; set; }
    public int Doors { get; set; }
    public string Color { get; set; }
}
Enter fullscreen mode Exit fullscreen mode
// An interface that contains all of the necessary methods to build
// the final product
public interface ICarBuilder
{
    void SetEngine(string engine);
    void SetSeats(int seats);
    void SetDoors(int doors);
    void SetColor(string color);
    Car Build();
}
Enter fullscreen mode Exit fullscreen mode
// Contains directions on how to build each of final product's instances
public class Director
{
    public Car MakeSuv(ICarBuilder builder)
    {
        builder.SetColor("White");
        builder.SetDoors(4);
        builder.SetEngine("Electric");
        builder.SetSeats(5);
        return builder.Build();
    }

    public Car MakeSportsCar(ICarBuilder builder)
    {
        builder.SetColor("Red");
        builder.SetDoors(2);
        builder.SetEngine("Diesel");
        builder.SetSeats(2);
        return builder.Build();
    }
}
Enter fullscreen mode Exit fullscreen mode
public class CarBuilder
{
    private readonly Car _car;

    public CarBuilder()
    {
        _car = new Car();
    }

    public void SetEngine(string engine)
    {
        _car.Engine = engine;
    }

    public void SetSeats(int seats)
    {
        _car.Seats = seats;
    }

    public void SetDoors(int doors)
    {
        _car.Doors = doors;
    }

    public void SetColor(string color)
    {
        _car.Color = color;
    }

    public Car Build()
    {
        return _car;
    }
}
Enter fullscreen mode Exit fullscreen mode

And that was it. Now we can instantiate our new Car objects

Director director = new Director();

ICarBuilder suvBuilder = new SuvCarBuilder();
Car suvCar = director.MakeSuv(suvBuilder);

ICarBuilder sportsCarBuilder = new SportCarBuilder();
Car sportCar = director.MakeSportsCar(sportsCarBuilder);
Enter fullscreen mode Exit fullscreen mode

When to use the Builder pattern

To get rid of a "telescoping constructor" anti-pattern.

When you want your code to be able to create different representations of some product

To construct complex objects

Pros and Cons of using the Builder pattern

✔️ You can construct objects step-by-step, defer construction steps or run steps recursively.

✔️ You can reuse the same construction code when building various representations of products.

✔️ Single Responsibility Principle. You can isolate complex construction code from the business logic of the product.

❌ The overall complexity of the code increases since the pattern requires creating multiple new classes.

Top comments (0)