<aside> 📚 📁 Category: Creational Pattern

</aside>

Overview

The Builder pattern 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.

<aside> â„šī¸ When to use: When you need to create an object with many optional parameters or when the construction process must allow different representations of the object being constructed.

</aside>

Problem

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 lots of parameters. Or even worse: scattered all over the client code.

Solution

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.

Structure

Implementation in C#

public class House
{
    public int Rooms { get; set; }
    public int Floors { get; set; }
    public bool HasGarage { get; set; }
    public bool HasSwimmingPool { get; set; }
    public bool HasGarden { get; set; }
    
    public override string ToString()
    {
        return $"House with {Rooms} rooms, {Floors} floors, " +
               $"Garage: {HasGarage}, Pool: {HasSwimmingPool}, Garden: {HasGarden}";
    }
}
public interface IHouseBuilder
{
    void Reset();
    void SetRooms(int rooms);
    void SetFloors(int floors);
    void BuildGarage();
    void BuildSwimmingPool();
    void BuildGarden();
    House GetResult();
}
public class ModernHouseBuilder : IHouseBuilder
{
    private House _house;
    
    public ModernHouseBuilder()
    {
        Reset();
    }
    
    public void Reset()
    {
        _house = new House();
    }
    
    public void SetRooms(int rooms)
    {
        _house.Rooms = rooms;
    }
    
    public void SetFloors(int floors)
    {
        _house.Floors = floors;
    }
    
    public void BuildGarage()
    {
        _house.HasGarage = true;
    }
    
    public void BuildSwimmingPool()
    {
        _house.HasSwimmingPool = true;
    }
    
    public void BuildGarden()
    {
        _house.HasGarden = true;
    }
    
    public House GetResult()
    {
        House result = _house;
        Reset();
        return result;
    }
}
public class HouseDirector
{
    private IHouseBuilder _builder;
    
    public HouseDirector(IHouseBuilder builder)
    {
        _builder = builder;
    }
    
    public void ConstructLuxuryHouse()
    {
        _builder.Reset();
        _builder.SetRooms(10);
        _builder.SetFloors(3);
        _builder.BuildGarage();
        _builder.BuildSwimmingPool();
        _builder.BuildGarden();
    }
    
    public void ConstructSimpleHouse()
    {
        _builder.Reset();
        _builder.SetRooms(3);
        _builder.SetFloors(1);
    }
}
// Without Director
var builder = new ModernHouseBuilder();
builder.SetRooms(5);
builder.SetFloors(2);
builder.BuildGarage();
builder.BuildGarden();
House house1 = builder.GetResult();

// With Director
var director = new HouseDirector(builder);
director.ConstructLuxuryHouse();
House luxuryHouse = builder.GetResult();

director.ConstructSimpleHouse();
House simpleHouse = builder.GetResult();

Console.WriteLine(house1);
Console.WriteLine(luxuryHouse);
Console.WriteLine(simpleHouse);

Implementation in Python

from abc import ABC, abstractmethod

class House:
    def __init__(self):
        self.rooms = 0
        self.floors = 0
        self.has_garage = False
        self.has_swimming_pool = False
        self.has_garden = False
    
    def __str__(self):
        return (f"House with {self.rooms} rooms, {self.floors} floors, "
                f"Garage: {self.has_garage}, Pool: {self.has_swimming_pool}, "
                f"Garden: {self.has_garden}")

class HouseBuilder(ABC):
    @abstractmethod
    def reset(self):
        pass
    
    @abstractmethod
    def set_rooms(self, rooms: int):
        pass
    
    @abstractmethod
    def set_floors(self, floors: int):
        pass
    
    @abstractmethod
    def build_garage(self):
        pass
    
    @abstractmethod
    def build_swimming_pool(self):
        pass
    
    @abstractmethod
    def build_garden(self):
        pass
    
    @abstractmethod
    def get_result(self) -> House:
        pass

class ModernHouseBuilder(HouseBuilder):
    def __init__(self):
        self.reset()
    
    def reset(self):
        self._house = House()
    
    def set_rooms(self, rooms: int):
        self._house.rooms = rooms
        return self
    
    def set_floors(self, floors: int):
        self._house.floors = floors
        return self
    
    def build_garage(self):
        self._house.has_garage = True
        return self
    
    def build_swimming_pool(self):
        self._house.has_swimming_pool = True
        return self
    
    def build_garden(self):
        self._house.has_garden = True
        return self
    
    def get_result(self) -> House:
        result = self._house
        self.reset()
        return result

# Usage with method chaining
builder = ModernHouseBuilder()
house = (builder
         .set_rooms(5)
         .set_floors(2)
         .build_garage()
         .build_garden()
         .get_result())

print(house)

Pros and Cons