Builder Pattern

Builder Pattern

The Story

Imagine you are a car designer. When you design a model you design it. But your customers are different and they want more different thing along with the basic things that the model of car provide. So to take care of all the customer design you will not design the model for each customer according to their need. The smart move will be to design a single model with his basic features and add the customers needs as optional thing. Now as you have the basic features ready, you can add each customers needs on that car by attaching it as a optional thing(not necessary for all customer). Like on the model one customer want RGB App LED Car Atmosphere Interior Light With Optic Fiber Cable and one want EVEN 2" Round HD Glass Convex 360° Wide Angle Side Rear View Mirrors. What you will do, you add this accessories to the basic car model the sell them.

The same thing happen in software's also. Like, to make a simple car you need four wheel, steering, an engine, covers and other stuffs. But what if you want a car with hybrid engine, smart control panel, music system, radio, calling feature etc.?

One simplest move will be creating one simple Car object and create subclass to cover all combinations and different configurations. But by this you ended up with multiple subclasses. Any new feature in a existing will again make one subclass and the hierarchy keep growing.

There's another approach that does not breed subclasses. You can create a giant constructor in the base Car class. Think it like a car model with the slot of all the modification. This indeed eliminates the growing hierarchy of subclasses, but create another problem. In most cases the parameters will be unused, making the constructor will look ugly. Like having all the slots for possible modification and are of no user. Like you have 100 customer of the car and only one customer want Bullbars, rest of the car do not want. So for the 99 car the slot is useless.

What is the solution?

What happen if we create separate class for each parts of Car the class and attach when one need in one time. Like for wheels we have wheel class and for engine we have engine class..... Also for Bullbar we have Bullbar class.

This is the Builder's pattern. It organizes object constructions into a set of steps. To create a object (in our case it is car) we will execute a series of steps on a builder object. The most interesting part is you do not have to call all of the step to create the object.

Example

Below is example of making different car with different requirement from a single Car class.

// The Product - Car
class Car {
    private String engine;
    private String transmission;
    private int airbags;
    private String color;
    private boolean sunroof;
    // Private Constructor
    private Car(CarBuilder builder) {
        this.engine = builder.engine;
        this.transmission = builder.transmission;
        this.airbags = builder.airbags;
        this.color = builder.color;
        this.sunroof = builder.sunroof;
    }
    @Override
    public String toString() {
        return "Car [Engine=" + engine + ", Transmission=" + transmission +
                ", Airbags=" + airbags + ", Color=" + color +
                ", Sunroof=" + sunroof + "]";
    }
    // The Builder
    public static class CarBuilder {
        private String engine;
        private String transmission;
        private int airbags;
        private String color;
        private boolean sunroof;

        public CarBuilder(String engine, String transmission) {
            this.engine = engine;
            this.transmission = transmission;
        }  

        public CarBuilder setAirbags(int airbags) {
            this.airbags = airbags;
            return this;
        }

        public CarBuilder setColor(String color) {
            this.color = color;
            return this;
        }

        public CarBuilder setSunroof(boolean sunroof) {
            this.sunroof = sunroof;
            return this;
        }

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

public class Main {
    public static void main(String[] args) {
        // Sports Car
        Car sportsCar = new Car.CarBuilder("V8", "Manual")
                .setAirbags(2)
                .setColor("Red")
                .setSunroof(true)
                .build();

        // Family Car
        Car familyCar = new Car.CarBuilder("V4", "Automatic")
                .setAirbags(6)
                .setColor("Blue")
                .setSunroof(false)
                .build(); 

        // SUV
        Car suv = new Car.CarBuilder("V6", "Automatic")
                .setAirbags(4)
                .setColor("Black")
                .setSunroof(true)
                .build();  

        System.out.println("Sports Car: " + sportsCar);
        System.out.println("Family Car: " + familyCar);
        System.out.println("SUV: " + suv);
    }
}

Above I three different car is created using one single Car class. We have used one static class to create the car builder according to the configuration. Below are the output.

Sports Car: Car [Engine=V8, Transmission=Manual, Airbags=2, Color=Red, Sunroof=true]
Family Car: Car [Engine=V4, Transmission=Automatic, Airbags=6, Color=Blue, Sunroof=false]
SUV: Car [Engine=V6, Transmission=Automatic, Airbags=4, Color=Black, Sunroof=true]

When to use?

  1. Complex Object Creation: When constructing objects with numerous optional and mandatory attributes.

  2. Avoid Constructor Overloading: Simplifies code by avoiding multiple constructors for different configurations.

  3. Step-by-Step Construction: Useful when the object needs to be built in a sequence of steps.

  4. Immutability: Ensures the final object is immutable by setting all attributes during creation.

  5. Flexibility: Ideal when the same construction process must produce different representations of an object.

  6. Readability: Improves code clarity, especially when creating objects with many parameters.

  7. Encapsulation of Construction Logic: Keeps the construction process separate from the object logic.

  8. Reusability: Allows reusing the builder to create variations of the same object.

  9. Examples: Commonly used in scenarios like building configurations, assembling UI components, or creating entities with varying features (e.g., cars, reports, or database queries).

Other resources