Builder
Separate the construction of a complex object from its representation so the same construction process can create different representations.
Build step by step.
The Builder pattern helps you construct a complicated object piece by piece, instead
of passing a dozen arguments to one giant constructor. You call methods like
.setSize(), .addTopping(), then .build() to get the finished
object.
# Problem: telescoping constructor — order and optionals are confusing. http = HttpRequest("GET", "https://api", None, {"q": "x"}, 30, True) # What was the 4th arg again? Easy to swap timeout and headers.
# Fix: Builder / fluent API — set only what you need, in any order. class HttpRequest: def __init__(self): self.method = "GET" self.url = "" self.headers = {} self.timeout = 30 def with_method(self, m): self.method = m; return self # return self => chain def with_url(self, u): self.url = u; return self def with_header(self, k, v): self.headers[k] = v; return self def build(self): assert self.url, "url required" return self # or freeze into an immutable req = (HttpRequest() .with_method("POST") .with_url("https://api") .with_header("Auth", "token") .build())
Telescoping constructors, immutability.
Builder solves the telescoping constructor problem: many overloaded constructors for different combinations of optional parameters. It also enables immutable objects — you gather all values in the builder, validate, then construct the final object once with no setters.
- Fluent interface — methods return the builder for chaining.
- Director (GoF) — optional object encapsulating a fixed build sequence/recipe.
- Validation — enforce required fields/invariants in
build().
GoF vs fluent, and relations.
- Classic GoF Builder emphasizes a Director driving abstract builders to produce different representations (e.g. build an HTML vs PDF document from the same steps).
- Effective Java Builder is the popular variant focused on readable, safe construction of one immutable object.
- vs Abstract Factory — Builder builds one complex object step by step; Abstract Factory returns families immediately.
- Thread safety — builders are usually not shared; the built object can be immutable and safely shared.
Interview Questions
A creational pattern that constructs a complex object step by step, separating how it's built from its final representation, so you can create the object incrementally and even produce different representations from the same steps.
Constructing objects that have many parameters (especially many optional ones) or require multi-step assembly, without huge unwieldy constructors and without exposing a half-built object.
A builder whose setter methods return the builder itself (this), allowing calls to be chained: builder.a().b().c().build(), which reads clearly and left-to-right.
A build() (or getResult()) method that assembles and returns the final product from the values collected in the builder.
Building an HTTP request (headers, method, body, timeouts), constructing a SQL/query object, assembling a configuration object, or String/StringBuilder-style text assembly — anywhere there are many optional parts.
Creational — it's concerned with the process of constructing objects.
When a class has many optional fields, you end up with a series of overloaded constructors — one taking 2 args, one 3, one 4, and so on — each calling the next. This is hard to read (callers must count positional arguments), error-prone (easy to transpose same-typed args), and combinatorially explodes. Builder replaces it with named, chainable setters and a single build step.
All the mutable "gathering" happens on the builder; when build() is called, it constructs the target object once (e.g. passing everything to a private constructor) with only final fields and no setters. The result is fully initialized and immutable, so it's safe to share and reason about, while callers still enjoyed a flexible, readable construction API.
The Director encapsulates a specific construction recipe — it knows the sequence of builder steps to make a particular product, and drives an (abstract) Builder through them. Clients ask the Director to build using a chosen concrete builder. It's optional; many modern builders omit the Director and let the client call steps directly, but it's useful when the same construction sequence should be reused across different builders/representations.
Prefer validating in build() (and/or in the product's constructor) so that required fields and cross-field invariants are checked when the complete object is assembled, failing fast before returning an invalid product. Per-setter validation can catch obvious bad values early, but only build() sees the full picture needed for invariants involving multiple fields. Validating in the constructor also protects the object even if someone bypasses the builder.
Factories focus on which object to create and typically return it in one call; Builder focuses on how a single complex object is assembled, over multiple steps. Factory Method/Abstract Factory pick a type/family; Builder configures the internals of one product incrementally and hands it back at the end. Builder is preferred when construction is complicated (many parts/optional config, ordering, validation), whereas factories are preferred when the main variability is the concrete type/family being instantiated.
Yes — that's the heart of the GoF version. By defining an abstract Builder interface with the same construction steps and multiple concrete builders, the same Director-driven sequence can yield different results: e.g. a document Director calls addTitle/addParagraph, and one builder emits HTML while another emits plain text or PDF. The construction process is shared; the representation differs by which concrete builder is used.
The GoF Builder emphasizes decoupling a construction process (driven by a Director) from the representation, with an abstract Builder and multiple concrete builders producing different products — the goal is reuse of the build sequence across representations. The Bloch builder (Effective Java) is a pragmatic idiom for one class: a static nested Builder with fluent setters and a build() that constructs an immutable instance, solving telescoping constructors and readability rather than multiple representations. There's usually no Director and no abstract builder hierarchy. In practice most "Builder pattern" usage today is the Bloch style (fluent, single immutable product), while the GoF style appears when you genuinely need the same steps to yield different outputs. Both share the core idea of staged construction, but differ in intent (representation independence vs. safe/readable object creation).
Plain fluent builders check required fields at runtime in build(). To enforce them at compile time you can use the step/staged builder (a.k.a. "phantom types" or interface-chaining) technique: put required steps behind separate interfaces so each required setter returns the interface exposing only the next required step, and only the final required step's return type exposes build() and the optional setters. This makes it impossible to compile a call that skips a required field, since the method to finish isn't available until all mandatory steps are done. The cost is more interfaces/boilerplate and less flexible call ordering, so it's used when construction correctness is critical. Code generation (e.g. annotation processors) can produce such staged builders to avoid hand-writing them.
It adds a parallel Builder class (more code and maintenance — every new field must be added in two places), a small runtime cost for the intermediate builder object, and can be overkill for simple objects with few parameters (where a constructor or named/optional parameters suffice). Mutable builders aren't thread-safe if shared, and a fluent API can obscure that required fields were forgotten until a runtime failure. In languages with named/default parameters (Python, Kotlin, C#), much of Builder's value is provided by the language, reducing the need for the pattern. So the trade-off is verbosity/complexity vs readability, immutability, and validation — worth it for genuinely complex construction, not for trivial value objects.
Method chaining (returning this/the next stage) is the mechanism often used to make a builder pleasant, but they're distinct ideas. A fluent interface is any API designed for readable chained calls (Fowler's term), which may or may not be a builder; a Builder is specifically about staged construction of a product and doesn't have to be fluent (it can use plain setters and a Director). Combining them gives the popular fluent builder. A subtlety: fluent chains complicate debugging/stack traces and can hide state, and returning this breaks down with inheritance (you need self-types/generics like <T extends Builder<T>> so subclass setters return the subclass type). So chaining is a stylistic enhancement of Builder, with its own trade-offs, not the essence of the pattern.
Naively, a subclass builder's fluent setters inherited from the parent return the parent builder type, breaking chaining when you interleave parent and child setters. The standard solution is a self-referential generic (curiously recurring template pattern): declare an abstract Builder<T extends Builder<T>> whose setters return self() (an abstract method each concrete builder overrides to return this as T). Concrete subclass builders bind T to themselves so inherited setters return the subclass type, preserving fluent chaining across the hierarchy. The abstract build() returns the product type (often covariantly overridden). This mirrors how class hierarchies with builders are handled in Effective Java, letting a Pizza.Builder be extended by NyPizza.Builder while keeping type-safe fluent calls — at the cost of some generic-heavy boilerplate.