HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMajor

Builder Pattern for Car class

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
carbuilderforclasspattern

Problem

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");
 }
}

Solution

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();
}

Code Snippets

public Car(String color, String wheels, int speed) {
    ....
}
Car mine = new Car(view.getCarColorDropDown().getSelectedValue(), view.getCarWheelsOptionRadioButton().getSelectedValue(), view.getCarSpeedSpinner().getValue());
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

}
Car.Builder builder = Car.builder();

builder.setColor("red");
builder.setWheels("rwd");
builder.setSpeed(200);

Car mine = builder.build();
Car[] productionLine = new Car[100];
for (int i = 0; i < productionLine.length; i++) {
    productionLine[i] = builder.build();
}

Context

StackExchange Code Review Q#63746, answer score: 27

Revisions (0)

No revisions yet.