CREATIONAL PATTERNS / OBJECT DESIGN BRIEF

Builder pattern

The Builder pattern constructs complex objects step by step with a readable, fluent API, then produces the final (often immutable) object with build().

BeginnerPhase 04 / Topic 3 of 6ResponsibilitiesCollaborationsExtensibility
01

Overview

The Builder pattern constructs complex objects step by step with a readable, fluent API, then produces the final (often immutable) object with build(). It replaces 'telescoping constructors' with many parameters, where calls like new Pizza(12, true, false, true, null) are unreadable and error-prone.

Builders also centralize validation: build() checks that required fields are set and combinations are valid before the object exists. The resulting object can be immutable and always valid. Director classes can encode standard recipes (a Margherita preset) on top of a builder.

Ordering a custom sandwich

You tell the counter: bread, then cheese, then toppings, then sauce, and finally 'that's it'. The sandwich is only made when you finish, and the staff can refuse invalid combinations.

02

When to use it

  • Objects with many optional parameters.
  • Immutable objects that need multi-step configuration.
  • Construction with validation across several fields.
03

Where it shows up in interviews

Complex configuration

Recognize it when: many optional fields or combinations.

  • Design an HTTP request builder
  • Design a pizza or coffee order
  • Design a report query builder
04

Where it is used in real software

Java StringBuilder and HttpRequest.newBuilder()

The JDK uses builders for strings and HTTP requests.

Lombok @Builder

Generates builder classes for Java data types.

Query builders

Knex, jOOQ, and SQLAlchemy build SQL step by step.

05

Key terms

Builder
Mutable helper that collects settings.
build()
Validates and returns the finished product.
Fluent interface
Methods return the builder to allow chaining.
Director
Encodes common build sequences.
Telescoping constructor
Many overloaded constructors with growing parameter lists.
06

How it works, step by step

  1. 1
    Make the product immutable

    Private constructor taking the builder.

  2. 2
    Create the builder with required fields

    In its constructor or static factory.

  3. 3
    Add fluent setters for optional fields

    Return this.

  4. 4
    Validate in build()

    Required fields and rules.

  5. 5
    Optionally add presets

    Director or static factory methods.

07

Constructor vs builder

HTTP request with method, URL, headers, body, timeout, retries

Step 1 / 3
ApproachCall siteProblem
Telescoping constructornew Request('POST', url, h, body, 5000, 3)Which number is which?
Setters on the objectr.setTimeout(5000) after creationObject can be half-built or mutated later
BuilderRequest.post(url).header(...).timeout(5s).build()Readable, validated, immutable

NOWApproach: Telescoping constructor | Call site: new Request('POST', url, h, body, 5000, 3) | Problem: Which number is which?

Builders make construction self-documenting and produce objects that are valid from birth.

08

Implementation

public final class Pizza {    private final int sizeInches;    private final String crust;    private final List<String> toppings;    private final boolean extraCheese;     private Pizza(Builder b) {        this.sizeInches = b.sizeInches;        this.crust = b.crust;        this.toppings = List.copyOf(b.toppings);        this.extraCheese = b.extraCheese;    }     public static Builder builder(int sizeInches) { return new Builder(sizeInches); }     public static final class Builder {        private final int sizeInches;        private String crust = "regular";        private final List<String> toppings = new ArrayList<>();        private boolean extraCheese;         private Builder(int sizeInches) { this.sizeInches = sizeInches; }        public Builder crust(String crust) { this.crust = crust; return this; }        public Builder topping(String t) { toppings.add(t); return this; }        public Builder extraCheese() { this.extraCheese = true; return this; }         public Pizza build() {            if (sizeInches < 8 || sizeInches > 18) throw new IllegalStateException("Size must be 8-18 inches");            if (toppings.size() > 6) throw new IllegalStateException("Max 6 toppings");            return new Pizza(this);        }    }} Pizza p = Pizza.builder(12).crust("thin").topping("basil").topping("olive").extraCheese().build();
09

Complexity and performance

ConstructionO(fields)

Plus validation.

Extra class1 builder per product

Can be generated.

10

Trade-offs

Boilerplate

Builders add code; for 2-3 fields, a constructor or named parameters are simpler.

Runtime vs compile-time checks

Missing required fields are caught at build() time unless you use staged builders.

11

Variants and related techniques

Staged (step) builder

Interfaces enforce required steps at compile time.

Named/optional parameters

Kotlin, Python, and TypeScript object literals often remove the need for builders.

12

Common mistakes

  • Returning a mutable product.

    Fix: Make the product immutable and copy collections.

  • No validation in build().

    Fix: Validate cross-field rules before creating the object.

13

Interview questions

When would you use a builder over a constructor?

When an object has many optional parameters or needs cross-field validation, and you want readable construction of an immutable, always-valid object.

Builder vs Factory?

Factories decide which class to create, usually in one call. Builders assemble one complex object step by step with configuration.

14

Practice problems

ProblemDifficultyWhat it trains
Builder for a coffee order with add-onsEasyFluent API.
SQL query builder with where, orderBy, limitMediumValidation.