Advanced Java September 20 ,2025

Builder Design Pattern in Java

Introduction

The Builder Pattern is a creational design pattern that allows constructing complex objects step by step, separating the construction process from the final representation. It is particularly useful when an object has many optional parameters or requires multiple steps for initialization.

Unlike constructors or factories, the Builder pattern provides a flexible and readable way to create objects without a telescoping constructor problem.

Real-world analogy:

  • Think of building a house: you don’t construct all parts in a single step. Instead, you add the foundation, walls, roof, and interior separately, adjusting optional features like a swimming pool or garden as needed.
  • Similarly, in software, the Builder pattern lets you create complex objects step by step, customizing each part along the way.

Problem Statement

Suppose we want to create a Burger object with multiple optional components:

class Burger {
    private String bun;
    private String patty;
    private String cheese;
    private String toppings;

    public Burger(String bun, String patty, String cheese, String toppings) {
        this.bun = bun;
        this.patty = patty;
        this.cheese = cheese;
        this.toppings = toppings;
    }
}

Issues:

  1. Telescoping Constructors: Adding optional parameters results in many overloaded constructors.
  2. Readability: Calling constructors with multiple parameters can be confusing:

    new Burger("Sesame Bun", "Beef", "Cheddar", "Lettuce, Tomato");
    
  3. Immutability and Safety: Complex objects may require safe construction with controlled initialization.

Implementation in Java

Step 1: Create the Product Class

public class Burger {
    private String bun;
    private String patty;
    private String cheese;
    private String toppings;

    // Private constructor to enforce Builder usage
    private Burger(BurgerBuilder builder) {
        this.bun = builder.bun;
        this.patty = builder.patty;
        this.cheese = builder.cheese;
        this.toppings = builder.toppings;
    }

    public void showBurger() {
        System.out.println("Burger [bun=" + bun + ", patty=" + patty +
                           ", cheese=" + cheese + ", toppings=" + toppings + "]");
    }

    // Step 2: Create the Builder Class
    public static class BurgerBuilder {
        private String bun;
        private String patty;
        private String cheese;
        private String toppings;

        // Builder methods for optional chaining
        public BurgerBuilder setBun(String bun) {
            this.bun = bun;
            return this;
        }

        public BurgerBuilder setPatty(String patty) {
            this.patty = patty;
            return this;
        }

        public BurgerBuilder setCheese(String cheese) {
            this.cheese = cheese;
            return this;
        }

        public BurgerBuilder setToppings(String toppings) {
            this.toppings = toppings;
            return this;
        }

        public Burger build() {
            return new Burger(this);
        }
    }
}

Step 2: Using the Builder

public class Main {
    public static void main(String[] args) {
        Burger burger = new Burger.BurgerBuilder()
                .setBun("Sesame Bun")
                .setPatty("Beef Patty")
                .setCheese("Cheddar")
                .setToppings("Lettuce, Tomato")
                .build();

        burger.showBurger();

        // Another burger with fewer options
        Burger vegBurger = new Burger.BurgerBuilder()
                .setBun("Whole Wheat")
                .setToppings("Lettuce, Tomato, Onion")
                .build();

        vegBurger.showBurger();
    }
}

Output:

Burger [bun=Sesame Bun, patty=Beef Patty, cheese=Cheddar, toppings=Lettuce, Tomato]
Burger [bun=Whole Wheat, patty=null, cheese=null, toppings=Lettuce, Tomato, Onion]

Explanation:

  • The BurgerBuilder allows flexible, step-by-step construction.
  • Optional fields can be omitted without requiring multiple constructors.
  • Chaining methods (setBun().setPatty()) improves readability and fluency.

Advantages of Builder Pattern

  1. Readable and Maintainable Code:
    • Complex object creation is clearer with chained builder methods.
  2. Immutability:
    • The product object can be made immutable by only providing getters and no setters.
  3. Flexible Object Construction:
    • Supports optional and mandatory parameters without telescoping constructors.
  4. Separation of Concerns:
    • Construction logic is separated from the object representation.
  5. Reusable Builder:
    • A single builder can create multiple variations of the same object.

Disadvantages

  1. Extra Code:
    • Requires creating an additional Builder class, which adds verbosity.
  2. Overhead for Simple Objects:
    • Not worth using for objects with few parameters or simple construction.
  3. Learning Curve:
    • Beginners may find method chaining and nested static classes confusing initially.

Real-World Analogies

  1. Burger or Pizza Creation:
    • Choose bun, patty, cheese, and toppings step by step.
  2. House Construction:
    • Build foundation, walls, roof, interior, and optional features like garden or swimming pool.
  3. Computer Configuration:
    • Choose CPU, RAM, storage, GPU, and peripherals.

When to Use

  • When an object has many optional parameters.
  • When constructor overloading becomes unreadable.
  • When object creation involves multiple steps or configurations.
  • When you want to make immutable objects with controlled construction.

Comparison with Other Patterns

PatternDifference from Builder
Factory MethodFactory returns a single product in one step; Builder builds step-by-step.
PrototypePrototype clones existing objects; Builder constructs objects from scratch stepwise.
Abstract FactoryAbstract Factory creates families of related objects; Builder focuses on one complex object.

Conclusion

The Builder Pattern is an elegant solution for creating complex, configurable objects in a readable and maintainable way.

  • It eliminates the need for multiple constructors.
  • It supports immutability and fluent interface design.
  • It is widely used in Java APIs like StringBuilder, Stream.Builder, and frameworks like Spring for building configuration objects.

     

Next Blog-  Structural Design Patterns in Java

 

Sanjiv
0

You must logged in to post comments.

Get In Touch

G06, Kristal Olivine Bellandur near Bangalore Central Mall, Bangalore Karnataka, 560103

+91-8076082435

techiefreak87@gmail.com