Back to Design Patterns
Creational Pattern

Builder Pattern

A creational design pattern that lets you construct complex objects step by step. The pattern allows you to produce different types and representations of an object using the same construction code.

Intermediate12 min read

Conceptual Overview

The Builder pattern is unique because it doesn't just create an object in one go; it breaks the process into independent steps. This is particularly effective when an object's construction involves many configuration options, some of which might be optional or mutually exclusive.

Structure Breakdown

  • Builder Interface: Declares the product construction steps common to all types of builders.
  • Concrete Builders: Provide different implementations of the construction steps.
  • Product: The resulting object. Products constructed by different builders don't have to belong to the same class hierarchy.
  • Director: Defines the order in which to call construction steps, so you can create and reuse specific configurations of products.

Why Choose the Builder?

Eliminate "Telescoping"

Avoid constructors with 10+ parameters where most are null. It makes the code cleaner and less error-prone.

Immutability

By using a builder, you can collect all data first and then instantiate a 'final' object that cannot be modified after creation.

Single Responsibility

It separates the complex construction logic from the business logic of the product class itself.

Step-by-Step Control

You can defer certain steps, run them recursively, or skip them based on runtime conditions.

Practical Implementation

// Product Class: The complex object being built
public class GamingPC {
    private final String processor;
    private final String ram;
    private final String gpu;
    private final int storageGB;
    private final boolean waterCooled;

    // Private constructor: Only accessible by the Builder
    private GamingPC(Builder builder) {
        this.processor = builder.processor;
        this.ram = builder.ram;
        this.gpu = builder.gpu;
        this.storageGB = builder.storageGB;
        this.waterCooled = builder.waterCooled;
    }

    // Static inner Builder class
    public static class Builder {
        private String processor; // Required
        private String ram;       // Required
        private String gpu = "Integrated"; // Optional with default
        private int storageGB = 512;        // Optional with default
        private boolean waterCooled = false; // Optional

        public Builder(String processor, String ram) {
            this.processor = processor;
            this.ram = ram;
        }

        public Builder setGpu(String gpu) {
            this.gpu = gpu;
            return this; // Return this for Method Chaining
        }

        public Builder setStorage(int gb) {
            this.storageGB = gb;
            return this;
        }

        public Builder enableWaterCooling() {
            this.waterCooled = true;
            return this;
        }

        public GamingPC build() {
            // Validation can happen here before returning product
            if (processor == null) throw new IllegalStateException("CPU required");
            return new GamingPC(this);
        }
    }
}

// Usage Example
GamingPC customRig = new GamingPC.Builder("AMD Ryzen 9", "64GB")
    .setGpu("RTX 4090")
    .setStorage(2048)
    .enableWaterCooling()
    .build();

Pros & Cons

Advantages

  • Constructs objects step-by-step or delays construction steps.
  • Reuse the same construction code for different representations.
  • Isolation of construction and assembly code.

Disadvantages

  • Increased complexity: Requires creating multiple new classes.
  • Overhead: Can be overkill for simple objects with few parameters.

Where You'll See It

Document Converters

An RTF Reader (Director) uses different Builders (PDFBuilder, HTMLBuilder, TeXBuilder) to convert a single document into various formats using the same parsing logic.

Unit Testing (Test Data Builders)

Creating complex mock objects for tests. Instead of setting 20 fields for a "User" object in every test, you use UserBuilder.withAdminPrivileges().build().

Keep Exploring

Mastered the Builder?

Learn how to create families of related objects without specifying their concrete classes.