0 XP · 0 scenes

Builder

Build a complex object one step at a time
The problem

The Pizza constructor takes eight parameters, most optional.

The idea

Give the object a helper, the builder.

Reach for it when

A constructor has many optional parameters

1

The picture from life

At a sandwich counter you say: wheat bread, add cheese, add lettuce, no onion, extra sauce, done. The sandwich is made in steps you choose, in any combination, and only when you say "done" do you get the finished sandwich.

2

The problem

What goes wrong when you do not use it.

The Pizza constructor takes eight parameters, most optional. Callers write Pizza("large", True, False, None, None, True, 2, "thin") and nobody remembers what each value means. Adding a ninth option breaks every call.

✗ Before
CallerPizza('large', True, False, None, None, True, 2, 'thin')Pizza8 constructor argswhich is which?
  • creates
A constructor with many positional values. Nobody can tell which True or None means what.
Reach for it when
  • A constructor has many optional parameters
  • The same steps can make different looks of one thing
  • The object must be valid only when fully built
3

The idea

How the pattern fixes it.

Give the object a helper, the builder. You call small, readable steps on it (withCheese(), withSize()) and finally build(). The builder checks everything and hands over the finished object.

✓ After
ClientPizzaBuildersize(s)cheese()topping(name)build()Pizzasize, cheesetoppingsdescribe()step by stepbuild()
  • calls / uses
  • creates
The builder collects choices step by step by name, then build() makes the finished object.

Who is who

Pizza
The finished product.
PizzaBuilder
Collects choices through chainable methods. build() returns a Pizza.
Director (optional)
A recipe that calls builder steps in a fixed order, e.g. margherita().

Build it in steps

  1. 1Create a builder class holding temporary fields.
  2. 2Add one method per option; return the builder itself.
  3. 3Add build() that validates and returns the product.
  4. 4Optionally add recipes for common combinations.
4

Watch it run

Step through the calls. Tap an arrow to jump to it.

ClientPizzaBuilderPizza1size('large')2cheese()3topping('olives')4build()5Pizza(large, cheese, [olives])6Pizza
Step 1/6

Each step has a clear name. It returns the builder, so calls chain.

size('large')
5

The code

Python and Java. Try writing the classes yourself from the diagram first.

class Pizza:
    def __init__(self, size, cheese, toppings):
        self.size = size
        self.cheese = cheese
        self.toppings = toppings

    def describe(self):
        extras = ", ".join(self.toppings) or "plain"
        return f"{self.size} pizza, cheese={self.cheese}, {extras}"


class PizzaBuilder:
    def __init__(self):
        self._size = "medium"
        self._cheese = False
        self._toppings = []

    def size(self, size):
        self._size = size
        return self                          # return the builder so calls chain

    def cheese(self):
        self._cheese = True
        return self

    def topping(self, name):
        self._toppings.append(name)
        return self

    def build(self):
        return Pizza(self._size, self._cheese, list(self._toppings))


def demo():
    return PizzaBuilder().size("large").cheese().topping("olives").topping("corn").build().describe()

What you gain

  • Readable creation: each step has a name
  • Optional parts without constructor explosion
  • The object can be immutable once built

What it costs

  • A whole extra class per product
  • Overkill for objects with two or three fields

You have already met it in

  • StringBuilder in Java
  • Lombok's @Builder and Protocol Buffers builders
  • Query builders such as Django's QuerySet chaining

Often mixed up with

  • Factory MethodFactory Method chooses which class; Builder shapes one complex object over several steps.
  • PrototypePrototype copies an existing object; Builder makes a new one from scratch.
6

Make it stick

Create an HttpRequestBuilder with method, url, header(), body() and timeout(). Make build() refuse a request with no url.

Confidence