It’s a Creational design pattern that provides a way to construct complex objects by separating object construction from its representation, so that same construction process creates different representations. For example, you can have a car with different types of wheels, different types of seats, different types of windows, etc.

Purpose: When we want to create complex object structures having multiple fields of which few of them are required(final) and rest of them are optional.

This post will have code snippets in Java.

Here is an example Car class having these required attributes make, model, manufacturingYear

public class Car {
  private final String make;
  private final String model;
  private final int manufacturingYear;

  public Car(String make, String model, int manufacturingYear) {
    this.make = make;
    this.model = model;
    this.manufacturingYear = manufacturingYear;
  }
  // Removed accessors for brevity
}

Let’s say there is a requirement to add new attributes like color, FuelType, Transmission and these are optional fields meaning not necessarily required during car object creation. Here’s the code after enhancement.

public class Car {
  private final String make;
  private final String model;
  private final int manufacturingYear;

  private enum FuelType {DIESEL, PETROL, HYBRID};
  private enum Transmission {AUTOMATIC, MANUAL};

  private String color;
  private FuelType fuelType;
  private Transmission transmission; 

  public Car(String make, String model, int manufacturingYear) {
    this(make, model, manufacturingYear, null, null, null);
  }

  public Car (String make, 
              String model, 
              int manufacturingYear, 
              String color, 
              FuelType fuelType, 
              Transmission transmission) {
    // Required fields
    this.make = make;
    this.model = model;
    this.manufacturingYear = manufacturingYear;
    // Optional fields
    this.color = color;
    this.fuelType = fuelType;
    this.transmission = transmission;
  }
  // Removed accessors for brevity
}

In the above snippet the caller has the flexibility to create the car object with or without the optional fields through overloaded constructors. But, this constructor overloading/telescopic constructor pattern in Java is generally considered antipattern. There are 2 problems having constructors loaded with multiple params.

  • It’s unclear for the caller code to recall the param sequence, readability is another issue.
  • Maintainability is also a problem, what if we add more new optional params in the future.

A better approach is to use the Builder pattern. Here is the improved version of the above code using Builder Pattern.

Note: There is no one way to create a Builder pattern, I will use one of the popular Style used in Java.

public class Car {
  // Required fields
  private final String make;
  private final String model;
  private final int manufacturingYear;

  public enum FuelType {
    DIESEL,
    PETROL,
    HYBRID
  }

  public enum Transmission {
    AUTOMATIC,
    MANUAL
  }

  private String color;
  private FuelType fuelType;
  private Transmission transmission;

  private Car(Builder builder) {
    this.make = builder.make;
    this.model = builder.model;
    this.manufacturingYear = builder.manufacturingYear;
    this.color = builder.color;
    this.fuelType = builder.fuelType;
    this.transmission = builder.transmission;
  }
  // Remvoved accessors for brevity

  public static class Builder {
    // Required fields
    private final String make;
    private final String model;
    private final int manufacturingYear;
    // Optional fields
    private String color;
    private FuelType fuelType;
    private Transmission transmission;

    public Builder(String make, String model, int manufacturingYear) {
      this.make = make;
      this.model = model;
      this.manufacturingYear = manufacturingYear;
    }

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

    public Builder fuelType(FuelType fuelType) {
      this.fuelType = fuelType;
      return this;
    }

    public Builder transmission(Transmission transmission) {
      this.transmission = transmission;
      return this;
    }

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

A sample snippet of Caller

public class BuilderExample {

  static void buildCars() {

    Car car1 =
        new Car.Builder("BMW", "X5", 2017)
            .color("red")
            .fuelType(Car.FuelType.PETROL)
            .transmission(Car.Transmission.AUTOMATIC)
            .build();

    Car car2 = new Car.Builder("Maruthi", "Baleno", 2022).build();

    Car car3 =
        new Car.Builder("Maruthi", "Baleno", 2022)
            .color("grandeurgrey")
            .fuelType(Car.FuelType.PETROL)
            .transmission(Car.Transmission.MANUAL)
            .build();
  }

  public static void main(String[] args) {
    buildCars();
  }
}

Here the Car class has delegated the object creation process to Builder instead of creating object directly.

References:

  1. Builder Pattern