How to implement a simple Builder Pattern in java

Isaac Ssemugenyi
5 min readJan 7, 2024
Photo by Clément Hélardot on Unsplash

In design patterns, the builder pattern is one of those patterns that fascinates me. It is a creational Design pattern that simplifies how we deal with complex objects and optional parameters. According to wikipedia, the intent of the builder design pattern is to separate the construction of a complex object from its representation.

Photo by Christian Holzinger on Unsplash

On Grepper, Atul Verma gives a few points on when to use the builder pattern:

  1. When the process involved in creating an object is extremely complex, with lots of mandatory and optional parameters
  2. When an increase in the number of constructor parameters leads to a large list of constructors
  3. When client expects different representations for the object that’s constructed

A spoiler alert, you may find that my implementation of the builder pattern differs from your expectations, my primary goal was to implement how to use Builder() class and build() method like in the okhttp3 package’s Request class.

Request request = new Request.Builder()
.url(myUrl)
.addHeader("Content-Type", "application/json")
.post(body)
.build();

In this article, we create a Car class that we use to create new Cars as we are going to explore:

  1. Working with no args constructor setting properties using setters.
  2. Working with an all args constructor passing parameters to the constructor.
  3. Lastly, we work with the builder pattern.

The intent of the builder design pattern is to separate the construction of a complex object from its representation

Using 'No args constructor'

We have created two classes, Car class and Main class. Inside the Car class we have all the properties that make up a car and it’s setters as well as the toString() method which helps us to print out the properties to the screen.

In the above code snippet, inside the Main class we create an instance of the Car class and we leverage the default no args constructor (created by the compiler in case no constructor is present in a class) to create the car1 instance and we set all the car properties on the car1 instance.

Output for the above code print out

2. Using 'all args constructor'

In this section, we add the required changes inside the existing implementation of the 'No args constructor' logic and finally we have an implementation that supports both working with setters and using the constructor to create instances of a Car. New changes are found at the end of the class.

In the above snippets, we modify the Car class to add the all args constructor and no args constructor . We then create the car2 instance from the Car class and directly pass it’s properties as parameters to the constructor.

Output for the above code

3. Builder pattern

In this section, we go ahead and add our builder code to the existing code as we want to maintain the old implementation of constructors functional. The builder pattern implementation presented uses a single class but this can be adjusted to deal with multiple complex classes.

In the above code snippets, In the Car.java class on line 62 we see the new code that implements the Builder pattern.

private Car(Builder builder){
this.make = builder.make;
this.name = builder.name;
......
}

Looking at this.make= builder.make;, it assigns the value of make from Builder class to the make property of the Car class, copying the value from the builder to the actual object being constructed. This happens for the rest of the properties.

At line 70 of the Car.java class, there is an inner Builder class inside the Car.java class. The Builder class has the public and static modifier which makes it accessible and instantiable outside of the Car class without creating an instance of the Car class.

Car.Builder newBuilder = new Car.Builder();

Inside the Builder class, we have a couple of encapsulated properties that correspond to the properties of the Car class and we methods that are accessible outside of the Builder class each returning the Builder class itself.

public static class Builder{
private String make;
.....
private List<String> countriesPresent = new ArrayList<>();
public Builder make(String make){
this.make = make;
return this;
}
.....
public Builder countriesPresent(List<String> countriesPresent){
this.countriesPresent = countriesPresent;
return this;
}
public Car build(){
return new Car(this);
}
}

Lastly in the Builder class we have a method build that returns the Car class.

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

The build method is a public method returning a Car type. The method is responsible for creating and returning an instance of the Car class. return new Car(this); this instantiates a new Car object, passes the current instance of builder ( this ) as an argument to the Car constructor. The purpose of returning the Car object is to finalize the construction of the Car object using the properties set in the builder and return the fully constructed Car instance.

This build method is called at the end of the chain of the setter methods in the builder pattern. The builder accumulates the values set by various setter methods, and when the client code is finished configuring the object, it calls the build method to create the actual instance of the desired object (in this case, a Car). The build method encapsulates the logic needed to construct the object, often by invoking a constructor with the accumulated values from the builder.

Now that we have the Builder class fully implemented, we then can use it in the Main class and set only the properties of choice using the respective Builder methods.

Car car3 = new Car.Builder()
.make("Ford")
.name("Mastunga")
.color("Red")
.model(2023)
.countriesPresent(List.of("Mexico", "Brazil"))
.build();

Here is the output when this piece of code is run.

Output for the above code

As we conclude, remove the countriesPresent method that takes in a list of countries and instead create a method that will allow us to add countries on a one by one basis and not a list.

Output for the above code

Builder Code Snippet

Final code snippet

With this, I hope you have learnt a thing or two about the Builder pattern and can use it in your day to day task.

Till next time, have a blessed day.

References

--

--

Isaac Ssemugenyi

Software Engineer with Stanbic Flyhub Uganda, Love programming with javascript, nodejs, vuejs, react, react-native and Java.