The Builder Pattern

Site Admin · 11 Sep 2026 · 13 views

The Builder Pattern

The Builder pattern separates the construction of a complex object from its representation. Instead of forcing callers through a constructor with many parameters, you build the object step by step through a dedicated builder. The result is readable, safe, and easy to extend.

The Problem: Telescoping Constructors

Classes with many optional fields tempt you to write overload after overload. This is the telescoping constructor anti-pattern.

public class HttpRequest {
    private final String url;
    private final String method;
    private final Map<String, String> headers;
    private final String body;
    private final int timeout;
    private final boolean followRedirects;

    public HttpRequest(String url, String method, Map<String, String> headers,
                       String body, int timeout, boolean followRedirects) {
        this.url             = url;
        this.method          = method;
        this.headers         = headers;
        this.body            = body;
        this.timeout         = timeout;
        this.followRedirects = followRedirects;
    }
}

Callers must remember the exact order of six parameters and supply values for options they do not care about. With N optional fields you would need 2^N overloads. This is unreadable and error-prone.

Building Step by Step

The Builder moves each option into a named method that returns the builder itself, enabling fluent chaining.

+-----------------------------+
|  HttpRequest.Builder        |
|  method(): Builder          |
|  header(): Builder          |
|  body(): Builder            |
|  timeout(): Builder         |
|  build(): HttpRequest       |
+-----------------------------+
        |
        |      method().header().body()
        |      (each returns Builder)
        |
        >  build()
+-----------------------------+
|  HttpRequest (immutable)     |
|  final fields, no setters   |
+-----------------------------+

The builder accumulates values; build() freezes them into an immutable product.

The Builder Class

The builder is usually a static nested class holding the same fields with sensible defaults.

public class HttpRequest {
    private final String url;
    private final String method;
    private final Map<String, String> headers;
    private final String body;
    private final int timeout;
    private final boolean followRedirects;

    private HttpRequest(Builder builder) {
        this.url             = builder.url;
        this.method          = builder.method;
        this.headers         = builder.headers;
        this.body            = builder.body;
        this.timeout         = builder.timeout;
        this.followRedirects = builder.followRedirects;
    }

    public static class Builder {
        private final String url;                  // required
        private String method = "GET";             // optional with default
        private Map<String, String> headers = new HashMap<>();
        private String body = "";
        private int timeout = 30000;
        private boolean followRedirects = true;

        public Builder(String url) {
            this.url = url;
        }

        public Builder method(String method) {
            this.method = method;
            return this;
        }

        public Builder header(String key, String value) {
            this.headers.put(key, value);
            return this;
        }

        public Builder body(String body) {
            this.body = body;
            return this;
        }

        public Builder timeout(int timeout) {
            this.timeout = timeout;
            return this;
        }

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

The product constructor is private and copies values from the builder, so every field can be final. Only the url is mandatory; the rest have defaults.

Using the Builder

Usage becomes self-documenting, and the IDE autocompletes only valid options.

HttpRequest request = new HttpRequest.Builder("https://api.example.com")
        .method("POST")
        .header("Content-Type", "application/json")
        .body("name:test")
        .timeout(5000)
        .build();

// request is immutable and safe to share
System.out.println(request);

Every setter returns the same Builder, so calls chain in any order. Because the product is immutable, it is safe to share across threads.

Real-World Use Cases

Java ships builders everywhere: StringBuilder, Stream.Builder, ProcessBuilder, and java.net.http.HttpRequest. Libraries such as OkHttp, Retrofit, and the MongoDB Java driver use builders for configuration. Any object with many optional parameters is a good candidate.

Key Points

  • Builder separates object construction from representation and gives clean code.
  • Named methods replace long parameter lists; every option is readable and optional.
  • The inner static Builder holds defaults and returns this from each setter.
  • The final build() method produces the immutable product object.
  • Why not telescoping constructors: exponential overloads, unclear ordering, unreadable calls.
Share this post:

Comments (0)

Please login or register to comment.