Java Builder Pattern
In this page:
Why Use Builder Pattern?
The Builder pattern constructs complex objects step by step instead of all at once, and it directly solves the 'telescoping constructor' problem, where a class ends up with a long chain of overloaded constructors just to support every combination of optional parameters.
Example: Why Use Builder Pattern?
public class Main {
static class Pizza {
String size; boolean cheese;
Pizza(String size, boolean cheese) { this.size = size; this.cheese = cheese; }
}
public static void main(String[] args) {
// Avoids a telescoping constructor for every combination of optional toppings
Pizza p = new Pizza("large", true);
System.out.println(p.size + " " + p.cheese);
}
}
Login to try C/C++/Java/PHP code in the editor
Inner Static Builder Class
A common Java implementation nests a static Builder class inside the class it constructs. The Builder accumulates configuration values through chained setter-style calls, and a final build() method uses those accumulated values to construct and return the actual outer-class instance.
Example: Inner Static Builder Class
public class Main {
static class Pizza {
String size; boolean cheese;
private Pizza(Builder b) { this.size = b.size; this.cheese = b.cheese; }
static class Builder {
String size; boolean cheese;
Builder size(String size) { this.size = size; return this; }
Builder cheese(boolean cheese) { this.cheese = cheese; return this; }
Pizza build() { return new Pizza(this); }
}
}
public static void main(String[] args) {
Pizza p = new Pizza.Builder().size("medium").cheese(true).build();
System.out.println(p.size + " " + p.cheese);
}
}
Login to try C/C++/Java/PHP code in the editor
Handling Mandatory and Optional Fields
To enforce that certain fields are mandatory, require them as parameters directly in the Builder's own constructor so an object can't be built without them. Optional fields, in contrast, can be set afterward through separate chainable methods that the caller is free to skip.
Example: Handling Mandatory and Optional Fields
public class Main {
static class Order {
final String item; String note;
private Order(Builder b) { this.item = b.item; this.note = b.note; }
static class Builder {
final String item; // mandatory -- required by the Builder's own constructor
String note; // optional
Builder(String item) { this.item = item; }
Builder note(String note) { this.note = note; return this; }
Order build() { return new Order(this); }
}
}
public static void main(String[] args) {
Order o = new Order.Builder("Book").note("Gift wrap").build();
System.out.println(o.item + " " + o.note);
}
}
Login to try C/C++/Java/PHP code in the editor
Thread Safety and Immutability
By omitting setter methods on the outer, built class entirely, the objects the Builder produces stay immutable and inherently thread-safe once constructed. You can also centralize validation of cross-field constraints inside the build() method itself, catching invalid combinations before the object ever comes into existence.
Example: Thread Safety and Immutability
public class Main {
static final class Point {
final int x, y; // no setters -- immutable once built
private Point(Builder b) {
if (b.x < 0 || b.y < 0) throw new IllegalArgumentException("negative coordinates");
this.x = b.x; this.y = b.y;
}
static class Builder {
int x, y;
Builder x(int x) { this.x = x; return this; }
Builder y(int y) { this.y = y; return this; }
Point build() { return new Point(this); }
}
}
public static void main(String[] args) {
Point p = new Point.Builder().x(3).y(4).build();
System.out.println(p.x + "," + p.y);
}
}
Login to try C/C++/Java/PHP code in the editor
Real-World Scenarios
Builders are heavily used in real-world code to configure HTTP requests with many optional headers and parameters, to construct complex SQL queries piece by piece, and to assemble application configuration objects that have dozens of independently optional settings.
Example: Real-World Scenarios
public class Main {
static class HttpRequestBuilder {
String url; String method = "GET"; String header;
HttpRequestBuilder url(String url) { this.url = url; return this; }
HttpRequestBuilder method(String method) { this.method = method; return this; }
HttpRequestBuilder header(String header) { this.header = header; return this; }
String build() { return method + " " + url + " [" + header + "]"; }
}
public static void main(String[] args) {
String request = new HttpRequestBuilder().url("/api/users").method("POST").header("Auth").build();
System.out.println(request);
}
}
Login to try C/C++/Java/PHP code in the editor
Chapter Quiz — Complete all 14 topics to unlock
0/14 topics done
Complete these topics first:
- Java Design Patterns Introduction
- Java Singleton Pattern
- Java Factory Pattern
- Java Observer Pattern
- Java Builder Pattern
- Java MVC Architecture
- Java Adapter Pattern
- Java Decorator Pattern
- Java Strategy Pattern
- Java Command Pattern
- Java Facade Pattern
- Java Proxy Pattern
- Java Template Method Pattern
- Java Repository Pattern