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 {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.
// 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;
}
}
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 {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:
// 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;
}
}
final Car toyota = new Car.Builder("Toyota","Avensis").power(108).torque(180).gears(6).build();As can be seen, it 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() {And now we can create new mutable Builders form existing immutable Car classes like this:
return new Builder(getBrand(), getType()).
power(getPower()).torque(getTorque()).gears(getGears()).color(getColor());
}
// This is the Car type that I can order at the car resellerThe toBuilder() and build() methods provide a means to switch between immutable and mutable objects seamlessly!
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();
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 {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:
// 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());
}
}
- 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 {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
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();
}
}
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").As you can see, my new car is a white electrical car with an upgraded battery package!
power(310).torque(600).gears(1).batteryCapacity(60).build();
final ElectricCar myNewCar = tesla.toBuilder().color("White").batteryCapacity(85).build();
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!