Builder
Assemble a complex object step by step, so the same construction sequence can produce different representations without a monstrous constructor.
The problem
Some objects need laborious, step-by-step initialization across many fields and nested
objects. A simple House needs four walls, a floor, a door, a couple of windows and a roof.
Then someone asks for a backyard, heating, plumbing and wiring.
The obvious fixes both fail. Subclass House for every combination and the hierarchy
metastasizes: add a porch style and it doubles. Write one giant constructor instead and you
get calls where most parameters are unused, because only a fraction of houses have swimming
pools.
The solution
Take the construction code out of the product class entirely and move it into a separate
object: the builder. Construction becomes a set of steps - buildWalls, buildRoof,
buildPool - and you invoke only the ones this particular object needs.
Different representations need different implementations of the same steps: cabin walls are timber, castle walls are stone. So write several concrete builders implementing the same step interface, and the same sequence of calls yields a cabin from one and a villa from another.
Optionally, extract that sequence into a director. The director decides the order of steps; the builder decides how each step is done. It is worth having when you replay the same recipes across the program, and it is entirely skippable otherwise. Note where the finished product comes from: the builder, not the director, because the director must stay ignorant of concrete product types.
- 1The client picks a representation by picking a builder. Stone today, timber tomorrow, same recipe either way.
- 2The client hands the builder to the director and names a configuration. Skipping this and calling the steps yourself is entirely legal.
- 3Every recipe starts with a blank product, so a reused builder cannot leak last week's garage into this house.
- 4The director knows the order of steps; the builder knows how each step is done. Neither knows the other's half.
- 5A villa gets a pool. A cabin recipe would simply not call this step, no null arguments required.
- 6The result comes from the builder, not the director, because the director must not know what type it just produced.
- 7The client receives a complete house. It was never handed a partially built one, which is the quiet safety benefit.
Structure
Builder interface, concrete builders, products, and an optional director. The products are the odd ones out here: unlike every other creational pattern, they need not share a class hierarchy or interface at all.
Code
Same example three ways: one construction sequence that produces either a house or the paperwork describing it.
When to use it
- You are staring down a telescoping constructor: ten optional parameters and a fan of overloads that all delegate to the worst one.
- You need several representations of a product built from similar steps that differ only in the details, such as stone versus wooden houses.
- You are constructing Composite trees or other recursive structures. Builder steps can call themselves, and you can defer a step without breaking the final product.
Pitfalls
- More classes, always. A builder interface plus one class per representation plus a director is real overhead. For a three-field object it is theatre.
- Forgetting to reset. Reuse a builder without clearing the product and last job's pool shows up in this job's cabin. Reset in the constructor and again after handing off.
- Directors that know too much. The moment a director references a concrete product type to return it, you have coupled it to the thing it was designed to be ignorant of.
Don't confuse it with
- Factory Method. A factory method is one call in, one product out. Builder is a conversation. Many designs start at Factory Method and migrate here when construction outgrows a single call.
- Abstract Factory. Families of related products, returned immediately, versus one complex product assembled over time. Abstract Factory also requires its products to share interfaces, which Builder pointedly does not.
- Fluent setters. Chained
withX()calls are a common Builder flavor, but chaining is a syntax choice. The pattern is the separated construction object, not the dots.
Check yourself
Which two symptoms does Builder exist to cure?