Skip to content

Design Pattern: Builder (Creational)

Objects with many optional parts

Builds on Constructors and Fluent API and annotations

Stage 3core2 min read

1. Overview

The Builder Pattern is a creational design pattern, which means it is about how objects get created. It builds a complex object one step at a time. The same steps can make different versions.

Formal Definition

"Builder pattern builds a complex object step by step. It separates the construction of a complex object from its representation, so that the same construction process can create different representations."

In Simpler Terms (The Chef Analogy)

Imagine ordering a custom burger. You choose the bread, patty, side, and drinks. The chef follows your instructions step by step. The Builder Pattern works the same way. You pick parts one at a time and skip the rest.


2. Class Diagram

The diagram shows the product (BurgerMeal) and the builder that lives inside it.

Loading diagram…

3. Understanding the Problem: "Telescoping Constructors"

A class with many optional parts often gets one constructor per combination. This is the Telescoping Constructor Anti-Pattern.

The "Bad" Code (Anti-Pattern)

class BurgerMeal {
    // Problem: To handle all combinations, you end up with "Constructor Bloat"
    public BurgerMeal(String bread, String patty) { ... }
    public BurgerMeal(String bread, String patty, String sides) { ... }
    public BurgerMeal(String bread, String patty, String sides, List<String> drinks) { ... }
    // Hard to read, error-prone, and inflexible. The repo keeps only the last one.
}

Key Issues Identified:

  1. Poor Readability: In new BurgerMeal("wheat", "non-veg", "none", null), you cannot tell what each value means without opening the class.
  2. Forced Nulls: Users must pass null for items they don't want.
  3. Maintenance Nightmare: Adding one field breaks every existing constructor call in the project.
  4. No Immutability: It is difficult to make fields final while also allowing flexible construction.

4. The Solution: Fluent Builder Pattern

A static nested class (a class inside another class) acts as the builder. You set the parts one by one, then call build().

import java.util.*;

class BurgerMeal {
    // 1. IMMUTABILITY: Fields are 'final'
    private final String breadType;
    private final String patty;
    private final boolean hasCheese;
    private final List<String> drinks;
    private final String side;

    // 2. PRIVATE CONSTRUCTOR: Prevents direct instantiation
    private BurgerMeal(BurgerMealWithBuilder builder) {
        this.breadType = builder.breadType;
        this.patty = builder.patty;
        this.hasCheese = builder.hasCheese;
        this.drinks = builder.drinks;
        this.side = builder.side;
    }

    // 3. STATIC NESTED BUILDER
    public static class BurgerMealWithBuilder {
        private String breadType; // Required
        private String patty;     // Required
        private boolean hasCheese;    // Optional
        private List<String> drinks;
        private String side;

        public BurgerMealWithBuilder(String breadType, String patty) {
            this.breadType = breadType;
            this.patty = patty;
        }

        // 4. FLUENT API: Return 'this' for method chaining
        public BurgerMealWithBuilder withCheese(boolean hasCheese) {
            this.hasCheese = hasCheese;
            return this;
        }

        public BurgerMealWithBuilder withSide(String side) {
            this.side = side;
            return this;
        }

        public BurgerMealWithBuilder withDrinks(List<String> drinks) {
            this.drinks = drinks;
            return this;
        }

        public BurgerMeal build() {
            return new BurgerMeal(this);
        }
    }
}

Each with... method sets one optional part and returns the same builder. build() passes the builder to the private constructor, which copies the values into the final fields.


5. Why This Is Better

AspectConstructor ApproachBuilder Pattern
Object ReadabilityPoor (nulls, long lists)Very good (reads like a sentence)
FlexibilityLow (all-or-nothing setup)High (configure only what’s needed)
MaintainabilityHard to scaleEasy to extend
SafetyHigh risk of positional errorsSafe (parts set by name)

6. When to Use vs. Avoid

Use When:

  • An object has multiple fields, especially many optional ones.
  • You want to create Immutable objects (they cannot change after they are built).
  • You want setup code that reads like a sentence (e.g., Domain Models).

Avoid When:

  • The class has only 1–2 fields (overkill).
  • The object is simple, mutable, or not complex enough to justify the boilerplate (extra setup code).

7. Real-World Examples

  1. Lombok's @Builder: A Java library that writes all this builder code for you when you add one annotation.
  2. Amazon Cart Configuration: Building a cart item involves many optional steps (Size, Color, Gift Wrap, Delivery Option) that fit a Builder well.
  3. Java's StringBuilder: Slightly different, but it also builds a result step by step with append calls.

In this topic

Done with this topic?

How well do you know it?

PreviousAbstract Factory Pattern Guide