Builder Pattern
Separate the construction of a complex object from its representation so that the same construction process can create different representations.
1What is Builder Pattern?
You build your sandwich step by step: choose bread, add proteins, vegetables, sauces. The same process creates vastly different sandwiches. You don't get a pre-made sandwich - you construct it piece by piece.
A house is built in stages: foundation, walls, roof, interiors. The same construction process can build a wooden cabin or a brick mansion. The builder knows the steps, but the result varies.
The Telescoping Constructor Problem
// Telescoping constructor
new Pizza(size, cheese, pepperoni,
bacon, mushrooms, onions,
olives, null, null, null);
// Which parameter is which?
// Many nulls for optional params
// Hard to read and maintain// Fluent builder
new Pizza.Builder("Large")
.addCheese()
.addPepperoni()
.addMushrooms()
.build();
// Clear, readable
// Only set what you need2Two Builder Variants
Director
└── Builder (interface)
├── ConcreteBuilder1
└── ConcreteBuilder2Product.builder()
.setA(...)
.setB(...)
.build();Fluent Builder is more common in modern codebases. Use it for objects with many optional parameters (like configuration objects, HTTP requests, SQL queries). Use GoF Builder when you have multiple representations of the same construction process.
3Implementation
// ========== Product ==========
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 constructor - only Builder can create
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;
}
// Getters...
public String getUrl() { return url; }
public String getMethod() { return method; }
@Override
public String toString() {
return method + " " + url + " (timeout=" + timeout + "ms)";
}
// ========== Static Builder Class ==========
public static class Builder {
// Required parameters
private final String url;
// Optional parameters with defaults
private String method = "GET";
private Map<String, String> headers = new HashMap<>();
private String body = null;
private int timeout = 30000;
private boolean followRedirects = true;
// Constructor with required params
public Builder(String url) {
this.url = url;
}
// Fluent setters - return this for chaining
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 ms) {
this.timeout = ms;
return this;
}
public Builder followRedirects(boolean follow) {
this.followRedirects = follow;
return this;
}
// Build method creates the immutable object
public HttpRequest build() {
// Validation can happen here
if (url == null || url.isEmpty()) {
throw new IllegalStateException("URL is required");
}
return new HttpRequest(this);
}
}
}
// ========== Usage ==========
public class Main {
public static void main(String[] args) {
// Simple GET request
HttpRequest get = new HttpRequest.Builder("https://api.example.com/users")
.build();
System.out.println(get);
// Complex POST request
HttpRequest post = new HttpRequest.Builder("https://api.example.com/users")
.method("POST")
.header("Content-Type", "application/json")
.header("Authorization", "Bearer token123")
.body("{\"name\": \"John\"}")
.timeout(5000)
.followRedirects(false)
.build();
System.out.println(post);
}
}GET https://api.example.com/users (timeout=30000ms) POST https://api.example.com/users (timeout=5000ms)
4Benefits and Drawbacks
- Avoids telescoping constructors
- Creates immutable objects
- Readable, self-documenting code
- Easy to add new optional params
- Validation before object creation
- More code to write
- Need separate Builder class
- Overkill for simple objects
- Requires discipline to maintain
In Java, use @Builder annotation from Lombok to auto-generate builder code. Saves boilerplate while keeping all benefits.
5Interview Follow-up Questions
Interview Follow-up Questions
Common follow-up questions interviewers ask