Java > Design Patterns in Java > Creational Patterns > Builder Pattern
Implementing the Builder Pattern for a Computer
This code snippet demonstrates the Builder pattern in Java for constructing a Computer
object. The Builder pattern separates the construction of a complex object from its representation, allowing the same construction process to create different representations. This is particularly useful when dealing with objects that have many optional or configurable components.
Defining the Computer Class
The A In the Computer
class represents the complex object we want to build. It has private fields for CPU, RAM, storage, graphics card, and power supply. The constructor is private to ensure that Computer
objects can only be created through the ComputerBuilder
.ComputerBuilder
inner static class is created. It contains the same fields as the Computer
class. The constructor of the builder takes the mandatory fields (CPU, RAM, and Storage) as arguments. Setter methods (e.g., graphicsCard()
, powerSupply()
) are provided for optional fields. These methods return the builder itself, allowing for method chaining. The build()
method creates a new Computer
object using the builder's values.main
method, we create a Computer
object using the ComputerBuilder
. We first create a ComputerBuilder
instance with the mandatory fields, then chain the setter methods for the optional fields, and finally call the build()
method to create the Computer
object.
public class Computer {
private String cpu;
private String ram;
private String storage;
private String graphicsCard;
private String powerSupply;
private Computer(ComputerBuilder builder) {
this.cpu = builder.cpu;
this.ram = builder.ram;
this.storage = builder.storage;
this.graphicsCard = builder.graphicsCard;
this.powerSupply = builder.powerSupply;
}
public String getCpu() {
return cpu;
}
public String getRam() {
return ram;
}
public String getStorage() {
return storage;
}
public String getGraphicsCard() {
return graphicsCard;
}
public String getPowerSupply() {
return powerSupply;
}
@Override
public String toString() {
return "Computer{" +
"cpu='" + cpu + '\'' +
", ram='" + ram + '\'' +
", storage='" + storage + '\'' +
", graphicsCard='" + graphicsCard + '\'' +
", powerSupply='" + powerSupply + '\'' +
'}';
}
public static class ComputerBuilder {
private String cpu;
private String ram;
private String storage;
private String graphicsCard;
private String powerSupply;
public ComputerBuilder(String cpu, String ram, String storage) {
this.cpu = cpu;
this.ram = ram;
this.storage = storage;
}
public ComputerBuilder graphicsCard(String graphicsCard) {
this.graphicsCard = graphicsCard;
return this;
}
public ComputerBuilder powerSupply(String powerSupply) {
this.powerSupply = powerSupply;
return this;
}
public Computer build() {
return new Computer(this);
}
}
public static void main(String[] args) {
Computer myComputer = new Computer.ComputerBuilder("Intel i7", "16GB", "1TB SSD")
.graphicsCard("NVIDIA RTX 3070")
.powerSupply("750W")
.build();
System.out.println(myComputer);
}
}
Concepts Behind the Snippet
The Builder pattern encapsulates the construction logic of a complex object. It separates the construction process from the object's representation, allowing the same construction process to create different representations. This is achieved by creating a separate builder class that is responsible for constructing the object step by step. This approach is particularly useful when an object has many optional parameters or when the construction process is complex and involves multiple steps.
Real-Life Use Case
Consider building a complex report. The report may have different sections, charts, tables, and formatting options. Using the Builder pattern, you can create a ReportBuilder
class to handle the construction process. The builder can have methods for adding sections, charts, setting formatting options, etc. Finally, the build()
method would create the final report object.
Best Practices
Immutable Objects: The Builder pattern often results in immutable objects, which are thread-safe and easier to reason about. Chaining: Use method chaining in the builder to improve readability. Validation: Validate the builder's state before building the object to prevent invalid objects from being created.
Interview Tip
When explaining the Builder pattern in an interview, emphasize that it's about separating the construction process from the object's representation. Mention its usefulness in handling complex objects with many optional parameters and its contribution to immutability and code readability.
When to Use the Builder Pattern
Use the Builder pattern when:
Alternatives
Factory Pattern: Use the Factory pattern when you need to create different types of objects based on some input. Abstract Factory Pattern: Use the Abstract Factory pattern when you need to create families of related objects. Prototype Pattern: Use the Prototype pattern when you need to create new objects by copying an existing object.
Pros
Separation of Concerns: Separates the construction logic from the object's representation. Immutability: Can result in immutable objects. Readability: Improves code readability with method chaining.
Cons
Increased Complexity: Introduces additional classes, which can increase code complexity.
FAQ
-
What is the main advantage of using the Builder pattern?
The main advantage is that it separates the construction of a complex object from its representation, allowing the same construction process to create different representations. It also handles objects with many optional parameters gracefully. -
Why is the constructor of the Computer class private?
The constructor of the Computer class is private to ensure that Computer objects can only be created through the ComputerBuilder, enforcing the pattern's control over object construction. -
What are mandatory parameters handled in the Builder pattern?
Mandatory parameters are typically passed to the Builder's constructor. Optional parameters are then set using setter methods in the builder.