19
\$\begingroup\$

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");
 }
}
\$\endgroup\$
26
\$\begingroup\$

There are three key features of the builder pattern that need to exist before the pattern becomes recognizable.

  1. the class you are tying to build should have a lot of properties/fields that you would normally want to pass as a constructor
  2. 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)
  3. 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();
}
\$\endgroup\$
  • \$\begingroup\$ edited to add template concept. \$\endgroup\$ – rolfl Sep 24 '14 at 12:24
  • \$\begingroup\$ In your example you have a static builder() method on Car. I have also seen direct Builder instantiations, using new Car.Builder(). Is there a reason to prefer one or the other? \$\endgroup\$ – nhaarman Sep 24 '14 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 Sep 24 '14 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 Sep 25 '14 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 Sep 25 '14 at 10:12
16
\$\begingroup\$

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 class Car.

  • 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 a Builder as that is the task for the method build().

  • Returning the Builder from the set methods in the Builder 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.

\$\endgroup\$
  • \$\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 Sep 25 '14 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 class Car... \$\endgroup\$ – Uwe Plonus Sep 25 '14 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 since build() would call that validation code in setters. I'd expect build() to do complex validation, but I'd also expect setColor 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 the build method was called some time later. The above answer shows an example of this with comments in the builder class. \$\endgroup\$ – Selali Adobor Sep 25 '14 at 18:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.