Skip to content

Builder Design Pattern (Creational)

Builder is a creational design pattern that lets you construct complex objects step by step. The pattern allows you to produce different types and representations of an object using the same construction code.

Imagine a complex object that requires laborious, step-by-step initialization of many fields and nested objects. Such initialization code is usually buried inside a monstrous constructor with dozens of parameters. Or even worse: scattered all over the client code.

For example, let’s think about how to create a House object. To build a simple house, you need to construct four walls and a floor, install a door, fit a pair of windows, and build a roof. But what if you want a larger, brighter house, with a heating system, plumbing, and electrical wiring?

The simplest solution is to extend the base House class and create a set of subclasses to cover all combinations of the parameters. But eventually, you’ll end up with a considerable number of subclasses. Any new parameter, such as the porch style, will require growing this hierarchy even more.

The Builder pattern suggests that you extract the object construction code out of its own class and move it to separate objects called builders.

The pattern organizes object construction into a set of steps (buildWalls, buildDoor, etc.). To create an object, you execute a series of these steps on a builder object. The important part is that you don’t need to call all of the steps. You can call only those steps that are necessary for producing a particular configuration of an object.

Some of the construction steps might require different implementations when you need to build various representations of the product. For example, walls of a cabin may be built of wood, but the castle walls must be built with stone.

In this case, you can create several different builder classes that implement the same set of building steps, but in a different manner. You can then use these builders with the same construction process (the Director) to produce different kinds of objects.

Think of a fast-food restaurant. The process of making a β€œValue Meal” is a builder process.

  • The Director is the cashier who takes your order and tells the kitchen what to make.
  • The Builder is the kitchen staff.
  • The Steps are: addMainItem(), addSideItem(), addDrink().
  • The Product is your meal.

Depending on whether you ordered a β€œKids Meal” or a β€œSuper Size Meal,” the builder uses the same steps but different components (small burger vs. big burger).

classDiagram
    class Director {
        - builder: Builder
        + construct()
    }
    class Builder {
        <<interface>>
        + reset()
        + buildStepA()
        + buildStepB()
        + buildStepZ()
    }
    class ConcreteBuilder {
        - result: Product
        + reset()
        + buildStepA()
        + buildStepB()
        + buildStepZ()
        + getResult() Product
    }
    class Product {
    }

    Director o-- Builder
    Builder <|.. ConcreteBuilder
    ConcreteBuilder ..> Product : creates
# The builder interface specifies methods for creating the
# different parts of the product objects.
interface Builder:
method reset()
method buildWalls()
method buildDoors()
method buildWindows()
method buildRoof()
method buildGarage()
# Concrete builders provide different implementations of the
# construction steps.
class HouseBuilder implements Builder:
private product: House
method reset():
this.product = new House()
method buildWalls():
# Build brick walls.
method buildDoors():
# Build wooden door.
# ... (other methods)
method getResult(): House:
return this.product
# The Director is only responsible for executing the building
# steps in a particular sequence.
class Director:
method makeSUV(builder: Builder):
builder.reset()
builder.buildWalls()
builder.buildDoors()
builder.buildRoof()
  • Use the Builder pattern to get rid of a β€œtelescoping constructor.” (A constructor with 10 optional parameters).
  • Use the Builder pattern when you want your code to be able to create different representations of some product (for example, stone and wooden houses).
  • Use the Builder to construct Composite trees or other complex objects.
  1. Clearly define the common construction steps for building all available product representations.
  2. Declare these steps in the base builder interface.
  3. Create a concrete builder class for each of the product representations and implement their construction steps.
  4. Think about creating a director class. It may encapsulate various ways to construct a product using the same builder object.
  5. The client code creates both the builder and the director.

Pros:

  • 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 isolate complex construction code from the business logic of the product.

Cons:

  • The overall complexity of the code increases since the pattern requires creating multiple new classes.
  • Many designs start by using Factory Method and evolve toward Abstract Factory, Prototype, or Builder.
  • Builder focuses on constructing complex objects step by step. Abstract Factory specializes in creating families of related objects.
  • You can use Builder when creating complex Composite trees because you can program its construction steps to work recursively.

// Product
public class Meal
{
public string Main { get; set; }
public string Drink { get; set; }
public override string ToString() => $"{Main} + {Drink}";
}
// Builder
public interface IMealBuilder
{
void BuildMain();
void BuildDrink();
Meal GetMeal();
}
// Concrete Builder
public class KidsMealBuilder : IMealBuilder
{
private Meal _meal = new Meal();
public void BuildMain() => _meal.Main = "Hamburger";
public void BuildDrink() => _meal.Drink = "Juice";
public Meal GetMeal() => _meal;
}
// Director
public class MealDirector
{
public Meal Construct(IMealBuilder builder)
{
builder.BuildMain();
builder.BuildDrink();
return builder.GetMeal();
}
}
// Usage
class Program
{
static void Main()
{
var director = new MealDirector();
var builder = new KidsMealBuilder();
var meal = director.Construct(builder);
Console.WriteLine(meal);
}
}