Memra

Static factories & the Builder pattern

Named constructors, the Builder for optional fields, and when each beats a telescoping constructor.

The problem: too many constructor parameters

When a class has many optional fields, you end up with a "telescoping constructor" — one overload per combination of optional parameters:

new Connection("host", 5432, "user", "pass", 30, true);
// Which argument is the timeout? Which is the port?

Two patterns solve this at different scales.

Static factory methods

A static factory is a static method that constructs and returns an instance. The constructor is kept private:

public final class Temperature {
    private final double celsius;
    private Temperature(double c) { this.celsius = c; }

    public static Temperature ofCelsius(double c)    { return new Temperature(c); }
    public static Temperature ofFahrenheit(double f) { return new Temperature((f - 32) * 5 / 9); }
    public static Temperature ofKelvin(double k)     { return new Temperature(k - 273.15); }
    public double toCelsius() { return celsius; }
}

Advantages over constructors: descriptive names (ofCelsius vs Temperature), can return a cached or subtype instance, and can enforce invariants before construction. Standard library examples: List.of(), Optional.of(), LocalDate.of(), Integer.valueOf().

The Builder pattern

A fluent Builder is the right tool when you have many optional fields and readability matters:

public static final class Builder {
    private final String to;
    private final String subject;
    private String body = "";
    private String cc;

    public Builder(String to, String subject) {
        this.to = to;  this.subject = subject;
    }
    public Builder body(String body) { this.body = body; return this; }
    public Builder cc(String cc)     { this.cc = cc;     return this; }
    public EmailMessage build()      { return new EmailMessage(this); }
}

// Usage:
EmailMessage msg = new EmailMessage.Builder("alice@example.com", "Hello")
    .body("Hi Alice!")
    .build();

Each setter returns this so calls can be chained. Required fields go in the Builder constructor; optional ones have setters with defaults. build() is the single place to validate invariants.

When to use which

| Situation | Reach for | |---|---| | Multiple unit-aware constructors | Static factory | | Caching / singleton | Static factory | | 4+ optional fields | Builder | | Simple required-only fields | Plain constructor | | Immutable value object, all required | record |

NORMAL ~/memra/learn/java-from-zero/static-factories-and-builder utf-8 LF