Builder
The problem
An Order needs a customer and at least one book, plus a handful of optional extras: a gift
message, delivery instructions, a promo code, express delivery. Modelling that with constructors
forces either one giant constructor full of nulls at every call site, or a “telescoping” pile of
overloads that grows combinatorially. Setters would fix readability but make the order mutable and
allow half-initialised objects.
The pattern
A Builder collects the fields step by step with a fluent, self-describing API, validates once in
build(), and produces an immutable Order:
Order order = Order.forCustomer(alice)
.add(refactoring)
.add(effectiveJava)
.giftMessage("Happy birthday!")
.express()
.build();
Benefits
- Readable call sites – every value is labelled by the method that sets it; no guessing what
the fourth
nullmeans. - Immutability – the built
Orderhas only final fields and defensive copies; it is safe to share across threads. - Single validation point –
build()rejects invalid combinations (an empty order) before an object ever exists in a bad state. - Evolvability – adding a new optional field is one builder method; existing call sites don’t change.
Seen in the wild
StringBuilder, java.net.http.HttpRequest.newBuilder(), Stream.builder(), Lombok’s
@Builder, protobuf message builders.
Implementation
Order – the immutable product, with its fluent Builder as a nested class.