Creating objects using the Builder Pattern
There are several patterns you can use when you want to create objects. In this post we will elaborate on some of them and we will learn the benefits of the
builder pattern and discover how this pattern can be used, both in base and extended Java classes. Before we talk about the
builder pattern we will first talk about some other means of creating and instantiating objects.
The Short Overview
Using the
builder pattern you can create classes like this:
Car toyota = new Car.Builder("Toyota", "Avensis").power(108).torque(180).gears(6).build();
instead of using the
bean pattern like this:
Car toyota = new Car("Toyota", "Avensis");
toyota.setPower(108);
toyota.setTorque(180);
toyota.setGears(6);
or instead of using the
telescope pattern like this:
Car Toyota = new Car("Toyota", "Avensis", 108, 180, 6);
The builder pattern is more scalable and more robust than the other patterns as we will learn below.
The Bean Pattern
The traditional
bean pattern relies on invoking an object's constructor (for example with its mandatory parameters) and then use the object's setters to initialize any optional parameters you might have. This sounds simple and robust, but, as we are about to see, this is more often than not a bad way to go about.
The following class outlines a
Car class implemented using the
bean pattern with the mandatory parameters "brand" and "type". There are also a bunch of additional optional parameters that we can set such as engine power and torque, the number of gears and the color of the car. Since we have not mention any specific units for the optional parameters, we assume that they are all in SI units. For example, power is measured in kW and torque in Nm.
public class Car {
// Required parameters
private final String brand;
private final String type;
// Optional parameters
private int power;
private int torque;
private int gears;
private String color;
public Car(String brand, String type, int power, int torque, int gears, String color) {
this.brand = brand;
this.type = type;
this.power = power;
this.torque = torque;
this.gears = gears;
this.color = color;
}
public Car(String brand, String type) {
this(brand, type, 0, 0, 0, null);
}
public String getBrand() {
return brand;
}
public String getType() {
return type;
}
public int getPower() {
return power;
}
public void setPower(int power) {
this.power = power;
}
public int getTorque() {
return torque;
}
public void setTorque(int torque) {
this.torque = torque;
}
public int getGears() {
return gears;
}
public void setGears(int gears) {
this.gears = gears;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
Now, if we want to create a new
Car we can do it like this:
final Car toyota = new Car("Toyota", "Avensis");
toyota.setPower(108);
toyota.setTorque(180);
toyota.setGears(6);
As can be seen, an object of the
Car type can not be created without the mandatory parameters "brand" and "type" (without "cheating" using reflection or similar methods at least). Furthermore, the mandatory parameters can not be changed after object instantiation, which is desirable most times.
However, there are several problems with the
bean pattern. Apparently, the
Car can be observed from outside while it is being instantiated (for example, by inserting code between the
setPower() and the
setTorque() method calls), allowing a partially instantiated
Car to be exposed, possibly to another Thread. This may potentially lead to hard-to-find bugs. Another problem is that there is no logical point of asserting the validity of the parameters and their relation. Suppose, for example, that you know that Toyota does not manufacture any car with a power output less than 80 kW if the car has 5 gears or more. How and when will you add that check?
However, the most prominent drawback, according to my view, is that the bean pattern
excludes the use of immutable objects. Many of Java's built-in objects like String and Integer are immutable and there are good reasons for that. An immutable class is, by definition, thread safe and many of its methods, like
hashCode() and
toString() can be optimized so that they are calculated only once. Furthermore, if all the member variables are
final, you ensure their invariants and the compiler also generates an error if you forget to initialize such a variable. Mutable classes can be a real nightmare when used as keys in
HashMaps or
HashSets because, if the key is changed once it has been hashed into the
Map/Set,
it can never be found again, but will still remain in the
Map/Set more or less as a dormant "zombie"!
The Telescope Pattern
Instead of using setters, we can have all the variables being initialized directly using one or several constructors. However, when you create new objects that have a sizable amount of member variables, some of which are optional, you will often end up with a considerable amount of constructors or static creation methods. For example, the well known
telescope constructor pattern can be used where one typically provides a single constructor with the mandatory parameters followed by an entourage of constructor variants with one, two, .., N of additional optional parameters. This likely leads to a silly amount of constructors that are hard to manage and overview. Also, each time you override such an object, you typically have to
duplicate all the constructors to expose them in the new extending class and add even more constructors to cover the new members introduced in the extending class.
If some parameters have the same type, like "power" and "torque" which both are
int, it will also prevent us from having separate constructors with these parameters, since these constructors
will have the same signature. It would, for example, be impossible to define both
public Car(String brand, String type, int power) and public
Car(String brand, String type, int torque) at the same time, because they both are
Car(String, String, int).
Consider the following
Car class that can be created using a traditional
telescope constructor pattern:
public class Car {
// Required parameters
private final String brand;
private final String type;
// Optional parameters
private final int power;
private final int torque;
private final int gears;
private final String color;
// Constructor will mandatory parameters
public Car(String brand, String type) {
this(brand, type, 0);
}
// Constructor will mandatory parameters and 1 optional parameter
public Car(String brand, String type, int power) {
this(brand, type, power, 0);
}
// Constructor will mandatory parameters and 2 optional parameters
public Car(String brand, String type, int power, int torque) {
this(brand, type, power, torque, 0);
}
// Constructor will mandatory parameters and 3 optional parameters
public Car(String brand, String type, int power, int torque, int gears) {
this(brand, type, power, torque, gears, null);
}
// Constructor will all parameters
public Car(String brand, String type, int power, int torque, int gears, String color) {
this.brand = brand;
this.type = type;
this.power = power;
this.torque = torque;
this.gears = gears;
this.color = color;
}
public String getBrand() {
return brand;
}
public String getType() {
return type;
}
public int getPower() {
return power;
}
public int getTorque() {
return torque;
}
public int getGears() {
return gears;
}
public String getColor() {
return color;
}
}
It becomes apparent why the pattern is called the
telescope pattern when we consider what happens if we invoke the constructor
Car(String brand, String type). It will call the constructor Car(String brand, String type, int power) which, in turn progressively will call constructors with more and more parameters until the constructor with all parameter finally is invoked.
We might now, for example, create a new
Car using this statement:
final Car toyota = new Car("Toyota", "Avensis", 108, 180, 6);
Now, we have obtained an immutable object, which is a big improvement over the previous
Car class created using the
bean pattern. Despite this, we are still suffering from the other drawbacks associated with the
bean pattern as described above. This is where the
builder pattern comes in.
The Builder Pattern
The
builder pattern comes with a price of some coding overhead, but brings many advantages over the previous patterns. Here is how the
Car class can look like when accompanied with an inner
Builder class:
public class Car {
// Required parameters
private final String brand;
private final String type;
// Optional parameters
private final int power;
private final int torque;
private final int gears;
private final String color;
public static class Builder {
// Required parameters
private final String brand;
private final String type;
// Optional parameters
private int power;
private int torque;
private int gears;
private String color;
public Builder(String brand, String type) {
this.brand = brand;
this.type = type;
}
public Builder power(int power) {
this.power = power;
return this;
}
public Builder torque(int torque) {
this.torque = torque;
return this;
}
public Builder gears(int gears) {
this.gears = gears;
return this;
}
public Builder color(String color) {
this.color = color;
return this;
}
public Car build() {
return new Car(this);
}
}
private Car(Builder builder) {
this.brand = builder.brand;
this.type = builder.type;
this.power = builder.power;
this.torque = builder.torque;
this.gears = builder.gears;
this.color = builder.color;
}
public String getBrand() {
return brand;
}
public String getType() {
return type;
}
public int getPower() {
return power;
}
public int getTorque() {
return torque;
}
public int getGears() {
return gears;
}
public String getColor() {
return color;
}
}
The neat thing with this is that now you can create a
Car simply by first creating its
Builder and then secondly invoking any number of its "setters" (in arbitrary order) finally followed with the
build() method. It looks like this:
final Car toyota = new Car.Builder("Toyota","Avensis").power(108).torque(180).gears(6).build();
As can be seen, i
t is much easier to read and understand what is going on. The pattern gives you the impression that you can use
named optional parameters even though this is not intrinsically supported by the Java language. There is only one constructor and there is also a very good spot where you can check the validity of the parameters, namely in the
build() method. When the latter method is called, you know all the parameters that has been set and if you detect any inconsistencies, you can elect to throw an
IllegalStateException telling what is wrong. The
build() method can also be made to derive or compute default values for unset parameters.
One obvious drawback with this solution is that you can not change the
Car once you
build() it, so rebuilding is impossible. You have to re-create the
Car from scratch if you want to change anything. In the next section we will learn how we can get around this limitation.
Obtaining New Immutables Using an Improved Builder Pattern
By definition, an immutable object can not be changed. But what can we do if we want to create a new object using an existing object that supports the builder pattern as described above? One very convenient way of doing this is to let our class implement a
toBuilder() method that will return a
Builder similar to the old
Builder, but not only using the constructor, but rather with all the values copied back from the already existing immutable class itself. To achieve this, we only need to add the following lines to our previous
Car class:
public Builder toBuilder() {
return new Builder(getBrand(), getType()).
power(getPower()).torque(getTorque()).gears(getGears()).color(getColor());
}
And now we can create new mutable
Builders form existing immutable
Car classes like this:
// This is the Car type that I can order at the car reseller
final Car toyota = new Car.Builder("Toyota", "Avensis").
power(108).torque(180).gears(6).build();
// I want a Black Car!
final Car myNewCar = toyota.toBuilder().color("Black").build();
The
toBuilder() and
build() methods
provide a means to switch between immutable and mutable objects seamlessly!
Extending Classes With Builders
Once you become familiar with the builder concept and start using the pattern, you will quickly encounter the challenge of extending
builder pattern classes. How can we do this in an efficient and appealing way? I will provide one solution here. First, we will have to modify the
Car class slightly to make it a bit more easily to extend.
public class Car {
// Required parameters
private final String brand;
private final String type;
// Optional parameters
private final int power;
private final int torque;
private final int gears;
private final String color;
public static class Builder {
// Required parameters
private final String brand;
private final String type;
// Optional parameters
private int power;
private int torque;
private int gears;
private String color;
public Builder(String brand, String type) {
this.brand = brand;
this.type = type;
}
public Builder power(int power) {
this.power = power;
return this;
}
public Builder torque(int torque) {
this.torque = torque;
return this;
}
public Builder gears(int gears) {
this.gears = gears;
return this;
}
public Builder color(String color) {
this.color = color;
return this;
}
public Car build() {
return new Car(this);
}
}
// Changed to protected
protected Car(Builder builder) {
this.brand = builder.brand;
this.type = builder.type;
this.power = builder.power;
this.torque = builder.torque;
this.gears = builder.gears;
this.color = builder.color;
}
public String getBrand() {
return brand;
}
public String getType() {
return type;
}
public int getPower() {
return power;
}
public int getTorque() {
return torque;
}
public int getGears() {
return gears;
}
public String getColor() {
return color;
}
// New methods below
protected Builder newBuilder() {
return new Builder(getBrand(), getType());
}
protected Builder decorate(Builder builder) {
return builder.power(getPower()).torque(getTorque()).
gears(getGears()).color(getColor());
}
public Builder toBuilder() {
return decorate(newBuilder());
}
}
Note that the
Car's constructor now is
protected instead of
private, allowing overriding classes to invoke it. We have also added and/or changed three new methods:
- newBuilder(), simply creates a new Builder that can be used to create the desired class, in this example a Car.
- decorate(), takes a Builder and "decorates" it with all the parameters it knows from the existing invariants.
- toBuilder(), now just simply decorates() a newBuilder(), resulting in a Builder with all parameters set from the corresponding invariants.
Note that we use getters whenever we refer to the parameters. This way, if we override one or several getters, we will get the correct result reflected in the new
Builder.
Now, suppose that I want to create an
ElectricCar class by extending the existing
Car class. The new class will have a new optional parameter called
batteryCapacity. In the example below I will show one possible way of implementing the new class:
public class ElectricCar extends Car {
private final int batteryCapacity; // Capacity in kWh
public static class Builder extends Car.Builder {
private int batteryCapacity;
public Builder(String brand, String type) {
super(brand, type);
}
public Builder batteryCapacity(int batteryCapacity) {
this.batteryCapacity = batteryCapacity;
return this;
}
@Override
public Builder color(String color) {
return (Builder) super.color(color);
}
@Override
public Builder gears(int gears) {
return (Builder) super.gears(gears);
}
@Override
public Builder power(int power) {
return (Builder) super.power(power);
}
@Override
public Builder torque(int torque) {
return (Builder) super.torque(torque);
}
@Override
public ElectricCar build() {
return new ElectricCar(this);
}
}
protected ElectricCar(Builder builder) {
super(builder);
this.batteryCapacity = builder.batteryCapacity;
}
public int getBatteryCapacity() {
return batteryCapacity;
}
@Override
protected Builder newBuilder() {
return new Builder(getBrand(), getType());
}
protected Builder decorate(Builder builder) {
super.decorate(builder);
return builder.batteryCapacity(getBatteryCapacity());
}
@Override
public Builder toBuilder() {
return (Builder) super.toBuilder();
}
}
We observe that the
build() method in the new
Builder class now creates an
ElectricCar rather then just a
Car. The new
Builder will inherit from the old
Car.Builder but all its optional "setters" now returns a
Builder that builds
ElectricalCars, however their bodies just calls the
Car.Builder's old existing methods. Looking at the
ElectricCar class itself, we see that it now has a new method called
newBuilder() that
, unsuprisingly, returns a new
Builder that builds
ElectricalCars instead of
Cars and a new
decorate() method that does everything that the
Car.decorate() method did,
plus decorating the new Builder with the getBatteryCapacity(). The
toBuilder() method body is unchanged but
it is cast so that it now returns a
Builder that builds
ElecticalCars.
Now you can use the new
ElectricalCar class as shown below:
final ElectricCar tesla = new ElectricCar.Builder("Tesla", "S").
power(310).torque(600).gears(1).batteryCapacity(60).build();
final ElectricCar myNewCar = tesla.toBuilder().color("White").batteryCapacity(85).build();
As you can see, my new car is a white electrical car with an upgraded battery package!
Future Improvement Potential When Inheriting Builder Classes
As can be seen, the optional "setter" methods in the
Builder all need to be overridden, creating seemingly unnecessary statements in our
Builder. It would be interesting to see if the overriding
builder pattern shown above could be implemented using generics for the
Builder, so that the optional "setter" methods in the extended
Builder could be inherited directly from the base
Builder without having to be overridden and cast.
Another point of improvement would be to let the
Car class itself use generics too, so that we do not have to override the
toBuilder() method.
Apparently, both the
Builder and the main base class (i.e.
Car and
ElectricalCar) contain the same member fields. Perhaps a new pattern can be found, that eliminate dual declaration of these
fields by letting one of the classes inherit them from the other class.
Read my
follow up post on the
Interface Builder Pattern how to make the
Builder Pattern even better!
Conclusion
The
builder pattern has many advantages over the
bean pattern and the
telescope pattern, but requires a bit more coding. Code readability and robustness is improved substantially and we may work with immutable classes. Classes are created using the
Builder's
build() method. By implementing a corresponding
toBuilder() method, we can easily switch back and forth between immutable and mutable classes combining the best of two worlds. Classes using the
builder pattern can relatively easy be extended indefinitely. There are still some potential to improve the patterns depicted in this article.
If you want to read more on the subject, I recommend reading chapter 2 in the book "Effective Java", second edition, by Joshua Bloch. I think it is a very good book.
Feel free to comment and improve on the examples above that I have shown in this post.
Happy object creation!