Separate the construction of a complex object from its representation, allowing the same process to create different representations.
The Builder pattern lets you construct complex objects step by step. It separates the "how to build" (construction process) from the "what to build" (representation), allowing the same construction steps to produce entirely different objects.
It's especially useful when an object has many optional parts, or when you want to prevent a constructor with a dozen parameters ("telescoping constructor" anti-pattern).
Imagine building a House object. It can have walls, a roof, windows, a garden, a pool, a garage... some optional, some required. A constructor with all these parameters becomes unmaintainable. Subclassing for every combination creates a class explosion.
The Builder extracts the construction logic into a separate object, letting you build step-by-step and only set what you need.
// Product public class House { private int walls, doors, windows; private boolean hasGarage, hasPool; // Setters (package-private, only builder sets) void setWalls(int w) { this.walls = w; } void setDoors(int d) { this.doors = d; } void setWindows(int w) { this.windows = w; } void setGarage(boolean g) { this.hasGarage = g; } void setPool(boolean p) { this.hasPool = p; } public String toString() { return "House[walls=" + walls + ", doors=" + doors + ", garage=" + hasGarage + ", pool=" + hasPool + "]"; } } // Builder interface public interface HouseBuilder { void buildWalls(); void buildDoors(); void buildWindows(); void buildGarage(); void buildPool(); House getResult(); } // Concrete Builder — standard house public class StandardHouseBuilder implements HouseBuilder { private House house = new House(); public void buildWalls() { house.setWalls(4); } public void buildDoors() { house.setDoors(2); } public void buildWindows() { house.setWindows(6); } public void buildGarage() { house.setGarage(false); } public void buildPool() { house.setPool(false); } public House getResult() { return house; } } // Director — controls construction sequence public class Director { private HouseBuilder builder; public Director(HouseBuilder b) { this.builder = b; } public void buildMinimalHouse() { builder.buildWalls(); builder.buildDoors(); } public void buildFullHouse() { builder.buildWalls(); builder.buildDoors(); builder.buildWindows(); builder.buildGarage(); builder.buildPool(); } } // --- Modern Java Fluent Builder (most common in practice) --- public class Pizza { private final String size, crust, sauce; private final boolean cheese, pepperoni; private Pizza(Builder b) { this.size = b.size; this.crust = b.crust; this.sauce = b.sauce; this.cheese = b.cheese; this.pepperoni = b.pepperoni; } public static class Builder { private final String size; // required private String crust = "thin"; private String sauce = "tomato"; private boolean cheese = true, pepperoni = false; public Builder(String size) { this.size = size; } public Builder crust(String c) { crust = c; return this; } public Builder pepperoni() { pepperoni = true; return this; } public Pizza build() { return new Pizza(this); } } } // Usage Pizza p = new Pizza.Builder("large") .crust("thick") .pepperoni() .build();
StringBuilder / StringBuffer — fluent builder for String constructionjava.util.stream.Stream.Builder — builds a stream step by step@Builder annotation — generates builder pattern automaticallyRequest.Builder — building HTTP requests with optional headers/paramsUriComponentsBuilder — building URIs step by stepbuild() before setting required fields — produces an invalid or inconsistent object with missing statebuild() twice or continues setting fields after the first build(), producing unexpected shared or mutated stateIllegalStateException if mandatory fields are missingbuilt = true flag; subsequent calls to build() or setters throw an exception// ❌ BREAKING — partial build, missing required field Pizza p = new Pizza.Builder("large") .build(); // sauce is null — invalid pizza! // ✅ FIX — validate inside build() public Pizza build() { if (sauce == null) throw new IllegalStateException("Sauce is required"); return new Pizza(this); } // ❌ BREAKING — reusing builder after build() Pizza.Builder builder = new Pizza.Builder("large").sauce("tomato"); Pizza p1 = builder.build(); builder.pepperoni(); // mutates already-built p1 state! Pizza p2 = builder.build(); // surprising result // ✅ FIX — invalidate builder after first build() private boolean built = false; public Pizza build() { if (built) throw new IllegalStateException("Builder already used"); built = true; return new Pizza(this); } // ✅ FIX — immutable product, only builder sets fields public class Pizza { private final String size, sauce; // final — no setters private Pizza(Builder b) { this.size = b.size; this.sauce = b.sauce; } // No setters exposed to clients }
this for chaining; build() creates the final immutable object.