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 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.

A 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.

In the 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:

  • An object has a complex construction process with many optional parameters.
  • You want to separate the construction process from the object's representation.
  • You want to ensure that the object is immutable.

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.