# Builder Design Pattern in Java: Build Complex Objects Step by Step

Imagine you are ordering a sandwich.

Bread and size are required, but the remaining ingredients are optional:

*   Cheese
    
*   Vegetables
    
*   Sauce
    
*   Extra paneer
    
*   Onion
    

You do not place your order like this:

```text
Large, true, false, true, true, false
```

Nobody can easily understand what each `true` or `false` represents.

Instead, you say:

> Give me a large sandwich, add cheese, add paneer, add mayonnaise, and do not add onion.

You are describing your sandwich step by step. After selecting everything, the final sandwich is prepared.

This is the basic idea behind the **Builder Design Pattern**.

* * *

## What is the Builder Design Pattern?

The Builder Pattern is a **creational design pattern** used to construct a complex object step by step.

It is useful when:

*   A class has many constructor parameters.
    
*   Some parameters are required and others are optional.
    
*   The same object can have different configurations.
    
*   Object creation should be readable and controlled.
    
*   Values must be validated before creating the final object.
    

In simple words:

> First configure the object using a Builder, then create the final object by calling `build()`.

* * *

## Understanding the Problem

Suppose we want to create a `Computer` object with the following properties:

*   Processor
    
*   RAM
    
*   Storage
    
*   Graphics card
    
*   Bluetooth
    

Without Builder Pattern, our class may look like this:

```java
class Computer {

    private String processor;
    private int ram;
    private int storage;
    private boolean graphicsCard;
    private boolean bluetooth;

    public Computer(
            String processor,
            int ram,
            int storage,
            boolean graphicsCard,
            boolean bluetooth) {

        this.processor = processor;
        this.ram = ram;
        this.storage = storage;
        this.graphicsCard = graphicsCard;
        this.bluetooth = bluetooth;
    }
}
```

We create an object like this:

```java
Computer computer =
        new Computer("Intel i7", 16, 512, true, false);
```

The code works, but it is confusing.

Look at these values:

```java
true, false
```

Without checking the constructor, we cannot tell:

*   Is `true` for the graphics card?
    
*   Is `false` for Bluetooth?
    
*   What is the correct parameter order?
    

If the class contains 10 or 15 fields, object creation becomes even more difficult.

* * *

## The Telescoping Constructor Problem

We might try to solve this problem using multiple constructors:

```java
public Computer(String processor) {
}

public Computer(String processor, int ram) {
}

public Computer(String processor, int ram, int storage) {
}

public Computer(
        String processor,
        int ram,
        int storage,
        boolean graphicsCard) {
}
```

As the number of optional fields increases, we need more constructor combinations.

This is known as the **Telescoping Constructor Problem**.

The Builder Pattern solves this by replacing confusing constructor parameters with clearly named methods.

* * *

## Builder Pattern Solution

```java
class Computer {

    private final String processor;
    private final int ram;
    private final int storage;
    private final boolean graphicsCard;
    private final boolean bluetooth;

    private Computer(Builder builder) {
        this.processor = builder.processor;
        this.ram = builder.ram;
        this.storage = builder.storage;
        this.graphicsCard = builder.graphicsCard;
        this.bluetooth = builder.bluetooth;
    }

    public void showDetails() {
        System.out.println("Processor: " + processor);
        System.out.println("RAM: " + ram + " GB");
        System.out.println("Storage: " + storage + " GB");
        System.out.println("Graphics Card: " + graphicsCard);
        System.out.println("Bluetooth: " + bluetooth);
    }

    public static class Builder {

        private final String processor;
        private int ram = 8;
        private int storage = 256;
        private boolean graphicsCard = false;
        private boolean bluetooth = false;

        public Builder(String processor) {
            this.processor = processor;
        }

        public Builder ram(int ram) {
            this.ram = ram;
            return this;
        }

        public Builder storage(int storage) {
            this.storage = storage;
            return this;
        }

        public Builder graphicsCard(boolean graphicsCard) {
            this.graphicsCard = graphicsCard;
            return this;
        }

        public Builder bluetooth(boolean bluetooth) {
            this.bluetooth = bluetooth;
            return this;
        }

        public Computer build() {
            validate();
            return new Computer(this);
        }

        private void validate() {

            if (processor == null || processor.isBlank()) {
                throw new IllegalArgumentException(
                        "Processor is required");
            }

            if (ram <= 0) {
                throw new IllegalArgumentException(
                        "RAM must be greater than zero");
            }

            if (storage <= 0) {
                throw new IllegalArgumentException(
                        "Storage must be greater than zero");
            }
        }
    }
}
```

### Main class

```java
public class Main {

    public static void main(String[] args) {

        Computer gamingComputer =
                new Computer.Builder("Intel i9")
                        .ram(32)
                        .storage(1000)
                        .graphicsCard(true)
                        .bluetooth(true)
                        .build();

        gamingComputer.showDetails();

        System.out.println();

        Computer officeComputer =
                new Computer.Builder("Intel i5")
                        .ram(16)
                        .storage(512)
                        .build();

        officeComputer.showDetails();
    }
}
```

### Output

```text
Processor: Intel i9
RAM: 32 GB
Storage: 1000 GB
Graphics Card: true
Bluetooth: true

Processor: Intel i5
RAM: 16 GB
Storage: 512 GB
Graphics Card: false
Bluetooth: false
```

* * *

## How Does the Builder Work?

Consider this code:

```java
Computer computer =
        new Computer.Builder("Intel i9")
                .ram(32)
                .storage(1000)
                .graphicsCard(true)
                .bluetooth(true)
                .build();
```

Let us understand it step by step.

### Step 1: Create the Builder

```java
new Computer.Builder("Intel i9")
```

At this point, the final `Computer` object has not been created.

Only a temporary Builder object exists with these values:

```text
processor = Intel i9
ram = 8
storage = 256
graphicsCard = false
bluetooth = false
```

The processor is required, while the remaining properties have default values.

### Step 2: Configure the Builder

```java
.ram(32)
.storage(1000)
.graphicsCard(true)
.bluetooth(true)
```

Every method updates one property inside the Builder.

For example:

```java
public Builder ram(int ram) {
    this.ram = ram;
    return this;
}
```

This changes the Builder’s RAM value from `8` to `32`.

### Step 3: Build the Final Object

```java
.build()
```

The `build()` method validates the configuration and creates the final object:

```java
public Computer build() {
    validate();
    return new Computer(this);
}
```

The complete Builder object is passed to the private `Computer` constructor.

```java
private Computer(Builder builder) {
    this.processor = builder.processor;
    this.ram = builder.ram;
    this.storage = builder.storage;
    this.graphicsCard = builder.graphicsCard;
    this.bluetooth = builder.bluetooth;
}
```

The constructor copies all values from the Builder to the final `Computer` object.

The complete flow is:

```text
Create Builder
      ↓
Set optional properties
      ↓
Validate configuration
      ↓
Call build()
      ↓
Create final Computer object
```

* * *

## Why Do Builder Methods Return `this`?

Consider this method:

```java
public Builder ram(int ram) {
    this.ram = ram;
    return this;
}
```

Here, `this` represents the current Builder object.

Returning `this` allows us to call another method on the same Builder:

```java
builder
    .ram(32)
    .storage(1000)
    .bluetooth(true);
```

This technique is known as **method chaining** or a **fluent API**.

Internally, the same Builder travels through the entire chain:

```text
Builder
   → ram()
   → same Builder
   → storage()
   → same Builder
   → build()
```

Without `return this`, we would have to write:

```java
Computer.Builder builder =
        new Computer.Builder("Intel i9");

builder.ram(32);
builder.storage(1000);
builder.bluetooth(true);

Computer computer = builder.build();
```

This also works, but method chaining is more readable.

* * *

## Why Is the Computer Constructor Private?

```java
private Computer(Builder builder)
```

The constructor is private so that outside code cannot directly create a `Computer`.

The caller must use the Builder:

```java
Computer computer =
        new Computer.Builder("Intel i7")
                .ram(16)
                .build();
```

This gives us controlled object creation because:

*   Required fields cannot be skipped.
    
*   Optional fields get default values.
    
*   Validation happens before object creation.
    
*   The caller cannot bypass the creation rules.
    

* * *

## Required and Optional Fields

In our example, the processor is required:

```java
public Builder(String processor) {
    this.processor = processor;
}
```

Therefore, the caller must provide it:

```java
new Computer.Builder("Intel i7")
```

The remaining fields are optional:

```java
private int ram = 8;
private int storage = 256;
private boolean graphicsCard = false;
private boolean bluetooth = false;
```

If the caller does not provide them, their default values are used:

```java
Computer basicComputer =
        new Computer.Builder("Intel i3").build();
```

This computer automatically receives:

```text
RAM = 8 GB
Storage = 256 GB
Graphics Card = false
Bluetooth = false
```

* * *

## Validation Before Object Creation

The Builder can validate the complete configuration before creating the final object.

```java
public Computer build() {
    validate();
    return new Computer(this);
}
```

For example:

```java
Computer computer =
        new Computer.Builder("Intel i7")
                .ram(-10)
                .build();
```

The Builder will reject this configuration:

```text
RAM must be greater than zero
```

Therefore, an invalid `Computer` object is never created.

* * *

## Builder and Immutability

The fields of `Computer` are declared as `final`:

```java
private final String processor;
private final int ram;
private final int storage;
```

The Builder is mutable while we are configuring it, but the final `Computer` is immutable.

```text
Mutable Builder → values can be configured

Immutable Computer → values cannot change after creation
```

This makes the final object safer because its state cannot change unexpectedly.

* * *

## Advantages of the Builder Pattern

### 1\. Readable object creation

Without Builder:

```java
new Computer("Intel i9", 32, 1000, true, false);
```

With Builder:

```java
new Computer.Builder("Intel i9")
        .ram(32)
        .storage(1000)
        .graphicsCard(true)
        .bluetooth(false)
        .build();
```

The Builder version clearly explains every value.

### 2\. Easy handling of optional fields

The caller only provides the properties that are needed.

### 3\. Parameter order does not matter

Both configurations produce the same result:

```java
builder.ram(16).storage(512);
```

```java
builder.storage(512).ram(16);
```

### 4\. Centralized validation

All validation rules can be placed inside `build()`.

### 5\. Supports immutable objects

The final object can have private final fields without setters.

### 6\. Different object configurations

The same Builder can create:

*   Basic computer
    
*   Office computer
    
*   Gaming computer
    

* * *

## Disadvantages of the Builder Pattern

Builder Pattern also has some disadvantages:

*   It introduces an additional class.
    
*   It requires more code than a simple constructor.
    
*   Builder and Product may contain the same fields.
    
*   It is unnecessary for simple objects.
    

For example:

```java
class Point {

    private final int x;
    private final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}
```

Creating a `Point` is already simple:

```java
Point point = new Point(10, 20);
```

Using Builder here would add unnecessary complexity.

* * *

## When Should We Use Builder?

Use the Builder Pattern when:

*   The class contains many fields.
    
*   Several fields are optional.
    
*   Constructor calls are difficult to understand.
    
*   The object has different possible configurations.
    
*   The final object should be immutable.
    
*   Complete validation is required before object creation.
    

Common real-world examples include:

*   HTTP request creation
    
*   User profile creation
    
*   Order creation
    
*   Database query creation
    
*   Test data creation
    
*   UI component configuration
    

* * *

## Builder Pattern vs Factory Pattern

| Factory Pattern | Builder Pattern |
| --- | --- |
| Decides which object to create | Decides how to configure an object |
| Usually creates an object in one step | Creates an object step by step |
| Useful for different implementations | Useful for different configurations |
| Example: Email, SMS or Push notification | Example: Basic, office or gaming computer |

Factory example:

```java
Notification notification =
        NotificationFactory.create("EMAIL");
```

Builder example:

```java
Computer computer =
        new Computer.Builder("Intel i7")
                .ram(16)
                .storage(512)
                .build();
```

The easiest way to remember the difference is:

> **Factory focuses on what object to create. Builder focuses on how to configure and construct it.**

* * *

## Conclusion

The Builder Pattern is useful when an object contains many required and optional properties.

It creates the object in four simple steps:

1.  Create a Builder with the required information.
    
2.  Configure optional properties using named methods.
    
3.  Call `build()` to validate the configuration.
    
4.  Receive the final fully constructed object.
    

The most important idea is:

> **The Builder is temporary and configurable. The final object is created only when the complete configuration is ready.**

That is why Builder Pattern makes object creation **readable, flexible, validated and controlled**.
