ποΈ Builder Design Pattern in Java β A Complete Guide
π Table of Contents
- Introduction
- Problem Statement
- What is the Builder Pattern?
- Structure and UML
- Real world Analogy
- Builder Pattern in Java β Code Example
- Example with Decorator Class
- Advantages
- Disadvantages
- When to Use
- When Not to Use
- Alternatives
- Common Pitfalls
- Conclusion
π§© Introduction
The Builder Design Pattern is a creational pattern used to construct complex objects step-by-step, especially when the object has many optional or configurable parameters. It helps in creating immutable objects in a clean and readable way.
π§ Problem Statement
Imagine you're creating a User object with many fields like:
User user = new User("John", "Doe", 30, "john@example.com", "123 Street", "Engineer", false, "India");
With this constructor, it's hard to:
- Know what each parameter means.
- Set only the required fields and ignore optional ones.
- Prevent errors in parameter ordering.
This is where the Builder Pattern shines.
ποΈ What is the Builder Pattern?
The Builder Pattern separates the construction of a complex object from its representation, allowing the same construction process to create different representations.
In simple terms, it allows building an object step-by-step using method chaining while keeping the object immutable once built.
π§ Structure and UML
Participants:
- Product β The object that is being built.
- Builder β Abstract interface defining the building steps.
- ConcreteBuilder β Implements the Builder steps.
- Director (optional) β Manages the construction process.
Client --> Director --> Builder --> ConcreteBuilder --> Product
π Real world Analogy
Imagine ordering a burger:
- You want a Veg burger π with cheese π§ but no lettuce π₯¬.
- A Builder lets you construct it step-by-step:
Burger burger = new Burger.Builder()
.addBun()
.addPatty("Veg")
.addCheese()
.build();
Each step configures a part of the final product.
π» Builder Pattern in Java β Code Example
1. Traditional Class with Many Parameters (Problematic)
public class User {
public User(String firstName, String lastName, int age, String email, String address, String occupation) {
// constructor with 6+ params
}
}
2. Builder Implementation
β
Product Class (User)
public class User {
// required fields
private final String firstName;
private final String lastName;
// optional fields
private final int age;
private final String email;
private final String address;
private User(Builder builder) {
this.firstName = builder.firstName;
this.lastName = builder.lastName;
this.age = builder.age;
this.email = builder.email;
this.address = builder.address;
}
public static class Builder {
private final String firstName;
private final String lastName;
private int age;
private String email;
private String address;
public Builder(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public Builder age(int age) {
this.age = age;
return this;
}
public Builder email(String email) {
this.email = email;
return this;
}
public Builder address(String address) {
this.address = address;
return this;
}
public User build() {
return new User(this);
}
}
}
β Client Usage
User user = new User.Builder("John", "Doe")
.age(30)
.email("john@example.com")
.build();
Example with Decorator Class
// Main.java
package org.example;
public class Main {
public static void main(String[] args) {
UserBuilder builder = new UserBuilder();
UserDirector director = new UserDirector(builder);
// Build a basic user
User basicUser = director.constructBasicUser("Ali", "ali@example.com");
// Build a premium user
User premiumUser = director.constructPremiumUser(
"Zeeshan Ali",
"zeeshan@example.com",
"Hyderabad, India"
);
System.out.println("Basic User: " + basicUser);
System.out.println("Premium User: " + premiumUser);
}
}
// User.java
package org.example;
public class User {
private String name;
private String email;
private String address;
public User(String name, String email, String address){
this.name = name;
this.email = email;
this.address = address;
}
@Override
public String toString() {
return "User : " +
"name='" + name + '\'' +
", email='" + email + '\'' +
", address='" + address + '\'' + "";
}
}
// UserBuilder.java
package org.example;
public class UserBuilder {
private String name;
private String email;
private String address;
public UserBuilder setEmail(String email) {
this.email = email;
return this;
};
public UserBuilder setName(String name) {
this.name = name;
return this;
};
public UserBuilder setAddress(String address) {
this.address = address;
return this;
};
public User build() {
return new User(name, email, address);
}
}
// UserDirector.java
package org.example;
// Director defines how to construct a specific type of User
public class UserDirector {
private final UserBuilder builder;
public UserDirector(UserBuilder builder) {
this.builder = builder;
}
// Build a standard user with minimal info
public User constructBasicUser(String name, String email) {
return builder
.setName(name)
.setEmail(email)
.setAddress("Not Provided")
.build();
}
// Build a premium user with full details
public User constructPremiumUser(String name, String email, String address) {
return builder
.setName(name)
.setEmail(email)
.setAddress(address)
.build();
}
}
β Advantages
| Benefit | Description |
|---|---|
| π§Ό Readable & Clean Code | Easy to understand which field is being set. |
| π¦ Handles Optional Parameters | Only set what you need. |
| π Immutable Objects | Builder typically returns fully initialized, immutable objects. |
| βοΈ Flexible Construction | You can reuse the same builder to construct different variants. |
| π§ͺ Improved Testability | Easier to mock and build test objects with only necessary fields. |
β Disadvantages
| Drawback | Description |
|---|---|
| π Boilerplate Code | You may need to duplicate fields in builder class. |
| π Overkill for Simple Objects | If object has 2β3 fields, Builder adds unnecessary complexity. |
| π Mutation Risk | If not implemented correctly, can expose mutable state. |
| π¦ Code Bloat | For every class, you may end up with another inner class. |
π When to Use
Use Builder Pattern when:
- Object has many fields, some of which are optional.
- You want to avoid telescoping constructors (constructors with many parameters).
- You want to create immutable objects with flexible construction.
- You have a class where certain combinations of fields are conditionally dependent.
π« When Not to Use
Avoid Builder Pattern when:
- The object has very few fields (e.g., 2β3).
- You donβt need to reuse building logic.
- Youβre okay with using setters or telescoping constructors for basic models.
π Alternatives
| Alternative | Use When |
|---|---|
| β Telescoping Constructors | Few parameters and simple construction. |
| β JavaBeans (Setters) | You need mutability and simple POJOs. |
| β Factory Pattern | Object creation is based on logic or parameters, not configuration chaining. |
| β Lombok @builder | You want Builder with less boilerplate. |
| β Record Classes (Java 16+) | For immutable data-holding objects with minimal logic. |
𧨠Common Pitfalls
| Pitfall | Fix |
|---|---|
β Forgetting build() method |
Ensure your Builder class always terminates with build(). |
| β Mutable shared builder instance | Avoid reusing builders across threads unless itβs stateless. |
| β Public setters in final class | Prefer private constructors and builder usage only. |
| β Misaligned fields between Builder and Product | Keep builder and product fields in sync. |
π Conclusion
The Builder Design Pattern is a powerful tool for creating complex, readable, and maintainable object construction logic in Java. It's a go-to pattern for domain models, DTOs, configuration objects, and anywhere flexibility in creation is important.
π‘ Use the Builder Pattern for clarity, safety, and scalabilityβespecially when constructors just donβt cut it.
π Explore More Design Patterns in Java
- π Mastering the Singleton Design Pattern in Java β A Complete Guide
- β οΈ Why You Should Avoid Singleton Pattern in Modern Java Projects
- π Factory Design Pattern in Java β A Complete Guide
- π§° Abstract Factory Design Pattern in Java β Complete Guide with Examples
- π Observer Design Pattern in Java β Complete Guide
- π Iterator Design Pattern in Java β Complete Guide
- π Adapter Design Pattern in Java β A Complete Guide
- π Strategy Design Pattern in Java β A Complete Guide
- π Decorator Design Pattern in Java β Complete Guide
More Details:
Get all articles related to system design
Hastag: SystemDesignWithZeeshanAli
Git: https://github.com/ZeeshanAli-0704/SystemDesignWithZeeshanAli
Top comments (0)