Facebook Pixel

Builder

The Builder pattern separates the construction of a complex object from the object itself. Callers invoke setter-style methods on a builder — one for each field they need — and call build() to obtain the finished object.

The pattern addresses a structural problem with constructors that have many parameters. A call such as new EmailMessage(to, from, subject, body, cc, bcc, replyTo, htmlBody, attachments, priority) requires the reader to count positions or scroll to the constructor definition to identify each argument. Optional parameters must still be passed as null or an empty list, and adding a new parameter forces every existing call site to be updated.

The same issue arises with HTTP request objects. A request can carry a URL, a method, a body, query parameters, custom headers, a timeout, a retry policy, and authentication credentials. Not every request uses all of these, yet a plain constructor requires a positional slot for each.

How the Builder solves it

A builder separates object construction from the object itself. You call setter-style methods on the builder — one for each field you actually need — and call build() at the end to get the finished object. Each setter returns self (or this), which lets you chain calls into a readable sequence.


The resulting call chain reads like a sentence: you see exactly which fields were set, in plain terms, with no positional ambiguity.

Each setter returns the builder itself, so the chain can be written in one expression before build() assembles the final immutable object.

Bad → Good

The telescoping constructor forces callers to pass every parameter in order, including the ones they do not need.

1# Bad: ten positional parameters, half of them optional nulls.
2# The caller must remember which position is which.
3class HttpRequest:
4    def __init__(
5        self,
6        url: str,
7        method: str,
8        headers: dict | None = None,
9        body: bytes | None = None,
10        timeout_ms: int = 5000,
11        retry_count: int = 0,
12        auth_token: str | None = None,
13        content_type: str | None = None,
14    ) -> None:
15        self.url = url
16        self.method = method
17        self.headers = headers or {}
18        self.body = body
19        self.timeout_ms = timeout_ms
20        self.retry_count = retry_count
21        self.auth_token = auth_token
22        self.content_type = content_type
23
24# At the call site, this is essentially unreadable:
25req = HttpRequest(
26    "https://api.payments.io/charge",
27    "POST",
28    None,
29    b'{"amount": 5000}',
30    10000,
31    3,
32    "Bearer tok_abc123",
33    "application/json",
34)

With a builder, only the fields you actually set appear in the call chain. The intent is clear at a glance, and adding a new optional field to HttpRequest does not break any existing call site.

Invest in Yourself
Your new job is waiting. 83% of people that complete the program get a job offer. Unlock unlimited access to all content and features.
Go Pro