I have some experience in Java. I am reading Effective Java by Joshua Bloch, the book talks about Builder pattern which I was unaware of. So,I have assembled a quick example of what I think builder pattern is all about. Is my implementation correct? Are there any gotchas in Builder pattern?
/* Object to be built using builder */
class Car{
public String wheels;
public String color;
public int speed;
}
class CarBuilder{
private Car car = new Car();
/* method to get car object */
public Car getCar(){
return car;
}
public CarBuilder setWheels(String brand){
car.wheels = brand;
return this;
}
public CarBuilder setColor(String color){
car.color = color;
return this;
}
public CarBuilder setSpeed(int speed){
car.speed = speed;
return this;
}
public Car build(){
return car;
}
}
public class builder{
public static void main(String args[]){
CarBuilder cb = new CarBuilder();
Car newcar = cb.setColor("Red").setSpeed(180).setWheels("MRF").build();
System.out.println("Your new car is "+newcar.color+" in color");
}
}
2 Answers 2
There are three key features of the builder pattern that need to exist before the pattern becomes recognizable.
- the class you are tying to build should have a lot of properties/fields that you would normally want to pass as a constructor
- you want those properties to be 'immutable' - constant for the life of the car (and this implies that there should not be 'setter' methods for those values)
- the class is constructed from many places so having a more convenient system is... convenient
In your case, you have three parameters, the color
, wheels
, and speed
for the car. These things can't be changed after the Car is built. You want a Car that has getColor()
, but not setColor(...)
.
The non-builder way to do this would be to have a single constructor that takes all the parameters:
public Car(String color, String wheels, int speed) {
....
}
and then call that constructor from everywhere. If there are a lot more parameters though, it gets messy to call the constructor:
Car mine = new Car(view.getCarColorDropDown().getSelectedValue(), view.getCarWheelsOptionRadioButton().getSelectedValue(), view.getCarSpeedSpinner().getValue());
Now imagine if you also want to select an interior color, automatic, or manual transmission, etc.
What happens when you have a lot of inputs is that it becomes hard to 'align' the values with the parameters of the class.... you have to cross reference them, and count the values to make sure you are putting the color in to the color parameter, etc.
Finally, what if you want some default values as well... or you need to validate combinations of choices.... like, you cannot select wheels
'abc' and not have speed
> 150? Validating that all in the constructor is ... hard.
The builder class is the solution to this. It makes the construction of the Car
class a sequential process... you add the data to a builder, it accumulates, validates, and isolates the inputs until you have captured all the information you need. Making sure the right parameters are given the right values is easy because the method has just one argument, and a logical name: setColor(view.getCarColorDropDown().getSelectedValue());
. Then, the builder organises the data in to a single call to the constructor (often private) of the Car class.
It is common to make the Builder class a sub-class of the class that is built, too.
Putting this all together, the builder has special permissions to access the Car constructor, the builder accumulates and validates the inputs in to changable values. It then calls the ugly constructor itself, creating a single, immutable class instance, and only one place in the code has to have the ugliness.
public class Car {
public static Builder builder() {
return new Builder();
}
// static inner class builder
public static class Builder {
private String builderColor = "white"; // default color
private String builderWheels = "round"; // default wheels
private int builderSpeed = 100; // default speed
private Builder() {
// inaccessible constructor, does nothing
}
public String getColor() {
return builderColor;
}
public void setColor(String color) {
// validate color....
this.builderColor = color;
}
public .... // more setters/getters
public Car build() {
return new Car(builderColor, builderWheels, builderSpeed);
}
}
// final (immutable) fields.
private final String color;
private final String wheels;
private final int speed;
// private constructor, only the builder calls this....
private Car(String color, String wheels, int speed) {
this.color = color;
this.wheels = wheels;
this.speed = speed;
}
public String getColor() {
return color;
}
.... // other getters
}
Now, how does one use the builder? Like this:
Car.Builder builder = Car.builder();
builder.setColor("red");
builder.setWheels("rwd");
builder.setSpeed(200);
Car mine = builder.build();
An often overlooked feature of a builder is that it is reusable as well, so, the above code can create many cars (it is a template of sorts)....
Car[] productionLine = new Car[100];
for (int i = 0; i < productionLine.length; i++) {
productionLine[i] = builder.build();
}
Or, you can change just small factors, like the VIN number... :
int vin = 123456000;
for (int i = 0; i < productionLine.length; i++) {
builder.setVIN(vin++);
productionLine[i] = builder.build();
}
-
\$\begingroup\$ edited to add template concept. \$\endgroup\$rolfl– rolfl2014年09月24日 12:24:54 +00:00Commented Sep 24, 2014 at 12:24
-
\$\begingroup\$ In your example you have a static
builder()
method onCar
. I have also seen directBuilder
instantiations, usingnew Car.Builder()
. Is there a reason to prefer one or the other? \$\endgroup\$nhaarman– nhaarman2014年09月24日 20:49:02 +00:00Commented Sep 24, 2014 at 20:49 -
1\$\begingroup\$ @NiekHaarman - I have seen it both ways, but chose to model my answer after the new Java8 Streams API (for some reason, perhaps because I was recently reading that documentation... consider:
DoubleStream.builder()
... where the Builder is an interface, not a concrete class... though it should not matter). \$\endgroup\$rolfl– rolfl2014年09月24日 21:02:55 +00:00Commented Sep 24, 2014 at 21:02 -
\$\begingroup\$
It is common to make the Builder class a sub-class of the class that is built, too.
Do you mean sub-class, like deriving from the class that is built, or sub-class, like inner class in the class that is built? \$\endgroup\$Dennis– Dennis2014年09月25日 09:09:05 +00:00Commented Sep 25, 2014 at 9:09 -
\$\begingroup\$ @Chips_100 - I mean a nested static class, like I have shown in the code example I gave. I did not use the term 'inner class' or 'nested class' because that is also easy to confuse because those are assumed to be instance (not static). I am happy to change the terminology I use, suggestions? \$\endgroup\$rolfl– rolfl2014年09月25日 10:12:29 +00:00Commented Sep 25, 2014 at 10:12
First of: the design of the class Car
is unusual. Members of a class should be kept private and according getter and setter should be defined.
To get to your class CarBuilder
which I review only:
why do you create the car when creating the builder? It is better to create the car in the according method (
build()
in your case), e.g.public Car build() { return new Car(); }
The attributes are normally collected in your
Builder
before the object is created. So you need the same set of properties as in your classCar
.The
build()
method is used to validate all collected attributes before creating the object, e.g.public Car build() { // validate your attributes Car car = new Car(); // set all attributes in your car return car; }
The method
Car getCar()
is wrong for aBuilder
as that is the task for the methodbuild()
.Returning the
Builder
from the set methods in theBuilder
is good practice as you can use the API fluent.
So in total my class would look like (if the class Car
is changed as mentioned above)
class CarBuilder {
private String wheels;
private String color;
private int speed;
public CarBuilder setWheels(String wheels) {
this.wheels = wheels;
return this;
}
public CarBuilder setColor(String color) {
this.color = color;
return this;
}
public CarBuilder setSpeed(int speed){
this.speed = speed;
return this;
}
public Car build(){
// validate your values e.g.
if (speed < 25) {
throw new IllegalValueException("Car is too slow.");
}
// further validation
Car car = new Car();
car.setWheels(wheels);
car.setColor(color);
car.setSpeed(speed);
return car;
}
}
The class IllegalValueException
would be an exception to be defined for your application.
-
\$\begingroup\$ I personally prefer moving the member specific validation code to the relevant setters. The most objective reason I can think of right now is that it's easier to debug if exceptions are thrown as soon as someone tries to pass invalid data in. They can't access the builder's current values to validate them themselves, and they shouldn't use get/setting the value multiple times for validation anyways (they can do custom validation before passing the value).
build
could still have validation code, but for the completed state of the car, such as making sure that all the values were set. \$\endgroup\$Selali Adobor– Selali Adobor2014年09月25日 02:04:52 +00:00Commented Sep 25, 2014 at 2:04 -
\$\begingroup\$ @AssortedTrailmix the validation in the
build()
method is meant to do complex validation (e.g. black cars have to have a minimal speed of 100). If it is a simple range validation then it would be btter in the classCar
... \$\endgroup\$Uwe Plonus– Uwe Plonus2014年09月25日 05:47:00 +00:00Commented Sep 25, 2014 at 5:47 -
\$\begingroup\$ I never said anything about "simple range validation". But regardless, putting that validation in
Car
would have the same end result sincebuild()
would call that validation code in setters. I'd expectbuild()
to do complex validation, but I'd also expectsetColor
on the builder to throw an exception as soon as I pass "Moo Cow This Is Not A Color" instead of letting me go as far as I wanted until thebuild
method was called some time later. The above answer shows an example of this with comments in the builder class. \$\endgroup\$Selali Adobor– Selali Adobor2014年09月25日 18:12:17 +00:00Commented Sep 25, 2014 at 18:12