Creational - Builder Pattern

Builder pattern lets you create complex object step by step.

Imagine you have a box of parts for different vehicles—wheels, seats, engines, lights, etc. You want to build a car, a bike, or a bus. You have a friendly robot helper (Builder) who asks you, step by step:

  • “Do you want wheels? How many?”

  • “Do you want an engine?”

  • “Do you want seats? How many?”

  • “Do you want lights?”

You tell the robot exactly how you want your vehicle by answering each question. When you’re done, the robot puts all your chosen parts together and gives you your custom vehicle, just the way you asked!

Builder Pattern does this in programming:

  • It lets you make complex things step by step (like building a car with 4 wheels, 2 seats, and bright lights).

  • You don’t have to know how everything fits or how to make it work—the robot (Builder) does it for you.

  • If you want to make a bike, you just tell the Builder to use 2 wheels and 1 seat, and it builds you a bike!

In short: With the builder pattern, you create amazing custom vehicles by telling the robot what parts you want, one after another, until your perfect car—or bike or bus—is ready. You build it your way, step by step!

Use the builder pattern when you have several variations of the same object, which needs to be created repeatedly.

class Vehicle {

    //required parameters
    private String engine;
    private int wheel;

    //optional parameter
    private int airbags;

    //only getters (optional)
    public String getEngine() {
        return engine;
    }

    public int getWheel() {
        return wheel;
    }

    public int getAirbags() {
        return airbags;
    }

    @Override
    public String toString() {
	return "Vehicle{" +
		"engine='" + engine + '\'' +
		", wheel=" + wheel +
		", airbags=" + airbags +
		'}';
    }

    //private constructor as the object will be created by Builder class only
    private Vehicle(VehicleBuilder builder) {
        this.engine = builder.engine;
        this.wheel = builder.wheel;
        this.airbags = builder.airbags;
    }

    //Builder class is public static inner class
    public static class VehicleBuilder {
        //required parameter
        private final String engine;
        private final int wheel;
        //optional parameter, no final
        private int airbags;

	//Only mandatory parameters passed
        public VehicleBuilder(String engine, int wheel) {
            this.engine = engine;
            this.wheel = wheel;
        }

        //setter method returns VehicleBuilder, not void
        public VehicleBuilder setAirbags(int airbags) {
            this.airbags = airbags;
            return this;
        }

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

Now , let's see the client class

// The Client
public class BuilderPatternClient {
    public static void main(String[] args) {
        Vehicle car = new Vehicle.VehicleBuilder("2500cc", 4)
            .setAirbags(2)
            .build();
        Vehicle bike = new Vehicle.VehicleBuilder("220cc", 2)
            .build();

        System.out.println(car.getEngine()); // 2500cc
        System.out.println(car.getWheel()); // 4
        System.out.println(car.getAirbags()); // 2
        System.out.println(car);

        System.out.println(bike.getEngine()); // 220cc
        System.out.println(bike.getWheel()); // 2
        System.out.println(bike.getAirbags()); // 0
        System.out.println(bike);
    }
}

Advantage

  • Use the Builder pattern to get rid of a “telescoping constructor” i.e. constructor with lots of optional parameters. To understand this, assume, this Vehicle class has many other optional features. So, to create a Vehicle instance, one need to have multiple variety of constructors.

//Multiple Constructors
public Vehicle(String engine, int wheel, int airbags, String feature1) {..}
public Vehicle(String engine, int wheel, int airbags, String feature1, feature2) {..}

//like this many other constructors... We don't want to do this.
Without Builder
With Builder
  • Use the Builder pattern when you want your code to be able to create different representations of some product

  • A builder doesn’t expose the unfinished product while running construction steps. This prevents the client code from fetching an incomplete result.

    • You get the object only when you call build() on VehicleBuilder

Disadvantage

  • The overall complexity of the code increases since the pattern requires creating multiple new classes.

Example of the builder pattern in the JDK is java.lang.StringBuilder.

  • StringBuilder lets you construct a string step by step by appending different parts (like words or characters) using the .append() methods.

  • You build up the final string, and then call .toString() to get the completed object.

  • This approach avoids the telescoping constructor problem and keeps object creation flexible and controlled.

StringBuilder builder = new StringBuilder();
builder.append("Hello, ");
builder.append("world!");
String result = builder.toString();

Other examples in the JDK:

  • java.lang.StringBuffer (thread-safe version)

  • java.nio.ByteBuffer and its variants (CharBuffer, IntBuffer, etc.)

  • java.util.stream.Stream.Builder

Rule of Thumb:

  • For simple objects, use constructors, setters, or static factory methods.

  • For many optional/complex parameters, use the Builder Pattern.

  • For language-specific improvements (Kotlin, Lombok), leverage those features to keep your code clean and simple!

Last updated

Was this helpful?