Builder#

tl;dr - 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.

The Builder pattern separates the process of constructing an object from the objects themselves. This separation allows the same construction process to create different representations.

Components#

  1. Builder: This is an interface that specifies methods for creating the parts of a product object.

  2. Concrete Builder: This is a class that implements the Builder interface, provides an interface for retrieving the product, and maintains the product through the building process.

  3. Director: This class is responsible for constructing the product using the Builder interface.

  4. Product: This represents the complex object that is being built.

Here’s an example of the Builder pattern in Java:

public interface Builder {
    void buildPartA();
    void buildPartB();
    Product getResult();
}

public class ConcreteBuilder implements Builder {
    private Product product = new Product();

    public void buildPartA() {
        product.add("PartA");
    }

    public void buildPartB() {
        product.add("PartB");
    }

    public Product getResult() {
        return product;
    }
}

public class Director {
    private Builder builder;

    public Director(Builder builder) {
        this.builder = builder;
    }

    public void construct() {
        builder.buildPartA();
        builder.buildPartB();
    }
}

public class Product {
    private List<String> parts = new ArrayList<>();

    public void add(String part) {
        parts.add(part);
    }

    public void show() {
        for (String part : parts) {
            System.out.println(part);
        }
    }
}

And here’s an example of the Builder pattern in Python with full type hinting in the 3.9+ way without the typing module:

class Builder:
    def build_part_a(self) -> None:
        pass

    def build_part_b(self) -> None:
        pass

    def get_result(self) -> 'Product':
        pass

class ConcreteBuilder(Builder):
    def __init__(self):
        self.product = Product()

    def build_part_a(self) -> None:
        self.product.add("PartA")

    def build_part_b(self) -> None:
        self.product.add("PartB")

    def get_result(self) -> 'Product':
        return self.product

class Director:
    def __init__(self, builder: Builder):
        self.builder = builder

    def construct(self) -> None:
        self.builder.build_part_a()
        self.builder.build_part_b()

class Product:
    def __init__(self):
        self.parts: list[str] = []

    def add(self, part: str) -> None:
        self.parts.append(part)

    def show(self) -> None:
        for part in self.parts:
            print(part)

In both examples, the Director object controls the construction process of a Product object. It only needs to know the type of builder to use for the process. The ConcreteBuilder knows how to assemble the product by implementing the Builder interface. The client only needs to instantiate the Director with a ConcreteBuilder, call the construct method to assemble the product, and call get_result to retrieve the product.

Joshua Bloch, in his book Effective Java, recommends using the Builder pattern to create objects that have many constructor parameters. The Builder pattern is also useful when you want to create different representations of the same object and when you want to create immutable objects (especially in Java).

--- Joshua Bloch's Builder ---