The Builder Pattern in Java

Contents

Introduction to the Builder Pattern

Design pattern in a simple meaning, is a way to design reusable object-oriented code. We can think of ways to design our classes, interfaces, enums and their members so that the code is reusable but flexible.

There are 23 most important design patterns, pioneered by the book Design Patterns: Elements of Reusable Object-Oriented Software, written by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides or Gang of Four for short. We will cover these design pattern one by one.

The builder pattern is one of the easiest and straightforward. Normally in Java we create an object of a class with constructors inside that class. That simple design closely ties the object creation process with the class. This approach is good only when the object is quite simple. The Builder pattern suggests moving the construction logic from object class to a separate class, the Builder class.

Original Implementation of the Builder in Java

In the original Builder pattern, we have:

– Product: this class represents the complex objects that we are building.

– Builder: an interface (or an abstract class) that defines the implementation for creating a Product object.

– Concrete Builder: a concrete class that must implement the Builder interface.

– Director: this class will create objects that we need.

Now in the src folder we create a Product.java. The Product here will be the Bike class, it can be considered a simple java bean without any constructor:

class Bike{
  private String type;
  private String brand;
  public String getType() {
    return type;
  }
  public void setType(String type) {
    this.type = type;
  }
  public String getBrand() {
    return brand;
  }
  public void setBrand(String brand) {
    this.brand = brand;
  }
}

Then also in Product.java we create an interface, this is the Builder interface:

interface Builder{
  void buildType();
  void buildBrand();
  Bike getBike();
}

This interface will define what to do to create a Bike.

Next in Product.java, we create two Concrete Builders, the RegularBikeBuilder and the ElectricBikeBuilder classes. These two class must implement the Builder interface:

class RegularBikeBuilder implements Builder{
  private final Bike bike;
  public RegularBikeBuilder() {
    bike = new Bike();
  }
  public void buildType() {
    bike.setType("Regular Bike");
  }
  public void buildBrand() {
    bike.setBrand("Giant");
  }
  public Bike getBike() {
    return bike;
  }
}

And the ElectricBikeBuilder:

class ElectricBikeBuilder implements Builder{
  private final Bike bike;
  public ElectricBikeBuilder() {
    bike = new Bike();
  }
  public void buildType() {
    bike.setType("Electric Bike");
  }
  public void buildBrand() {
    bike.setBrand("BMW");
  }
  public Bike getBike() {
    return bike;
  }
}

Finally, we create a Test.java in src folder:

public class Test {
  public static void main(String[] args){
    //create regular bike
    var builder1 = new RegularBikeBuilder();
    BikeDirector d1 = new BikeDirector(builder1);
    d1.createBike();
    Bike bike1 = d1.getBike();
    System.out.println(bike1.getType() + " - "
        + bike1.getBrand());
    //create electric bike
    var builder2 = new ElectricBikeBuilder();
    BikeDirector d2 = new BikeDirector(builder2);
    d2.createBike();
    Bike bike2 = d2.getBike();
    System.out.println(bike2.getType() + " - "
        + bike2.getBrand());
  }
}

Run the code and we have the output:

Regular Bike - Giant
Electric Bike - BMW

So in the original Builder pattern, the Bike object creation process is totally decoupled from the Bike class.

– The Bike class defines the Bike object.

– The Builder interface and the Builder classes define the way to create Bike objects.

– The BikeDirector will actually create the Bike objects.

New Implementation of the Builder in Java

Another form of Builder pattern, though not exactly the same as the original design in Gang of Four’s book, but we should take a look. First we create a Bike.java in src folder and a public class, the Bike class:

public class Bike{
  private final String type;
  private final String brand;
  //this is a inner static class, which is also
  //a member of Bike class
  public static class Builder{
    //required field
    private String type = "";
    //optional field
    private String brand = "";
    public Builder(String type) {
      this.type = type;
    }
    //the method returns a Builder object
    //so later we can chain the methods
    public Builder buildBrand(String brand){
      this.brand = brand;
      return this;
    }
    public Bike build(){
      return new Bike(this);
    }
  }
  //the Bike constructor is private to force
  //using the Builder.
  private Bike(Builder builder){
    type = builder.type;
    brand = builder.brand;
  }
  //the Bike class only has getters,
  //all the variables are built from builder
  public String getType() {
    return type;
  }
  public String getBrand() {
    return brand;
  }
}

Now, in Test.java:

public class Test {
  public static void main(String[] args){
    var bike1 = new Bike.Builder("Regular Bike")
        .buildBrand("Giant")
        .build();
    System.out.println(bike1.getType() + " - "
        + bike1.getBrand());
  }
}

The output:

Regular Bike - Giant

Note that the Bike object is immutable, we only have getters in Bike class. This is much simpler and has the same effect as the original Builder pattern.

Now the steps for this simple Builder pattern:

– We create the target class (here it is the Bike class)

– Then in Bike class, we create a public static inner class Builder with the members mimicking the parent class Bike. If some fields are required, then we create a constructor for those fields.

– In the Builder class, each optional field is built with a method that returns the same Builder object.

– In the Builder class, we create the build() method that returns the object that we need (here is the Bike object)

– Back to the Bike class, we create a private constructor to initialize the Bike object’s fields (values are taken from the inner Builder class). The reason that we choose the private constructor to force using the Builder to create a Bike object.

– Then we can create getters as usual.

Leave a Reply