creational

Builder

Separate the construction of a complex object from its representation so the same process can create different representations.

Some objects have a lot of fields, and most are optional.

Put them all in a constructor and you get calls like new Pizza(12, true, false, true, 2, false). Nobody can read that. Which true was extra cheese? Write one constructor per combination and you end up with dozens.

Builder lets you set fields one at a time, by name, and then build the object at the end. The call reads like a description of what you want.

Ordering a custom pizza — you pick crust, sauce, toppings one by one. The kitchen only bakes it once you say done.

Key Concepts

1
A separate builder object collects the values. Each setter returns the builder itself, so calls can be chained: .size(12).cheese(true).build().
.size(12).cheese(true).build()
2
Nothing is created until you call build(). That method runs the validation and returns the finished object. Because the object is only assembled once, at the end, its fields can be final and it can be immutable.
build()final
3
build() is also where you check that required fields were actually set. Without that check, the pattern happily gives you a half-filled object.
build()

When to use it

  • Objects with many optional parameters
  • Object construction requires multiple steps
  • You need immutable objects that are easy to configure

Watch out for

  • Nothing enforces that required fields were set — build() must validate, or you get objects that compile but are half-constructed at runtime
  • Verbose: a builder roughly doubles the code of the class it builds, so for three or four fields a constructor or static factory is clearer
  • Reusing one builder instance across threads, or calling build() twice and mutating in between, quietly produces shared or inconsistent state
java
public class HttpRequest {
    private final String url;
    private final String method;
    private final Map<String, String> headers;

    private HttpRequest(Builder b) {
        this.url = b.url;
        this.method = b.method;
        this.headers = b.headers;
    }

    public static class Builder {
        private final String url;
        private String method = "GET";
        private Map<String, String> headers = new HashMap<>();

        public Builder(String url) { this.url = url; }
        public Builder method(String m) { this.method = m; return this; }
        public Builder header(String k, String v) { headers.put(k, v); return this; }
        public HttpRequest build() { return new HttpRequest(this); }
    }
}