1

Consider the following example of builder pattern:

import java.util.Objects;

public class Vehicle {
    private final String brand;
    private final int wheels;
    private final int doors;
    private final int maxSpeed;

    private Vehicle(Builder builder) {
        this.brand = Objects.requireNonNull(builder.brand, "brand");
        this.wheels = Objects.requireNonNull(builder.wheels, "wheels");
        this.doors = Objects.requireNonNull(builder.doors, "doors");
        this.maxSpeed = Objects.requireNonNull(builder.maxSpeed, "maxSpeed");
    }

    public static Builder builder() {
        return new Builder();
    }

    public void display() {
        System.out.println("brand = " + getBrand());
        System.out.println("wheels = " + getWheels());
        System.out.println("doors = " + getDoors());
        System.out.println("maxSpeed = " + getMaxSpeed());
    }

    public String getBrand() {
        return brand;
    }

    public int getWheels() {
        return wheels;
    }

    public int getDoors() {
        return doors;
    }

    public int getMaxSpeed() {
        return maxSpeed;
    }

    public static class Builder {
        private String brand;
        private Integer wheels;
        private Integer doors;
        private Integer maxSpeed;

        Builder() {
        }

        public Builder setBrand(String brand) {
            this.brand = brand;
            return this;
        }

        public Builder setWheels(int wheels) {
            this.wheels = wheels;
            return this;
        }

        public Builder setDoors(int doors) {
            this.doors = doors;
            return this;
        }

        public Builder setMaxSpeed(int maxSpeed) {
            this.maxSpeed = maxSpeed;
            return this;
        }

        public Builder of(Vehicle vehicle) {
            this.brand = vehicle.brand;
            this.wheels = vehicle.wheels;
            this.doors = vehicle.doors;
            this.maxSpeed = vehicle.maxSpeed;
            return this;
        }

        public Vehicle build() {
            return new Vehicle(this);
        }
    }
}

When we create an object of Vehicle type, we have:

Vehicle vehicle = new Vehicle.Builder()
                .setBrand("Mercedes")
                .setWheels(4)
                .setDoors(4)
                .setMaxSpeed(250)
                .build();

On the other hand, one can simply return this in each setter of the class Vehicle, e.g.:

public class Vehicle {
    private final String brand;
    private final int wheels;
    private final int doors;
    private final int maxSpeed;

    private Vehicle(String brand, int wheels, int doors, int maxSpeed){
        this.brand = brand;
        this.wheels = wheels;
        this.doors = doors;
        this.maxSpeed = maxSpeed;
    }

    public Vehicle() {
    }

    public String getBrand() {
        return brand;
    }

    public int getWheels() {
        return wheels;
    }

    public int getDoors() {
        return doors;
    }

    public int getMaxSpeed() {
        return maxSpeed;
    }

    public Vehicle setBrand(String brand) {
        this.brand = brand;
        return this;
    }

    public Vehicle setWheels(int wheels) {
        this.wheels = wheels;
        return this;
    }

    public Vehicle setDoors(int doors) {
        this.doors = doors;
        return this;
    }

    public Vehicle setMaxSpeed(int maxSpeed) {
        this.maxSpeed = maxSpeed;
        return this;
    }

    public void display() {
        System.out.println("brand = " + getBrand());
        System.out.println("wheels = " + getWheels());
        System.out.println("doors = " + getDoors());
        System.out.println("maxSpeed = " + getMaxSpeed());
    }
}

We can create an object of Vehicle type by:

Vehicle vehicle = new Vehicle();
vehicle.setBrand("Mercedes")
       .setDoors(4)
       .setMaxSpeed(250);

In my opinion, the second approach produces less code than the conventional builder pattern. Nevertheless, I know many libraries making use of builder pattern instead. So, what are the pros of builder pattern comparing to the second approach, which is literally less complicated?

Truong
  • 201
  • 1
  • 5
  • 11
  • 3
    It allows a `Vehicle` to be constructed without a brand, speed etc. So it doesn't seem sensible. – Henry Twist Jun 19 '21 at 09:26
  • 1
    If you code the Builder or fluent API yourself you are doing something wrong in the first place. Use e.g. lombok to auto-generate the builder. Apart from that: what Henry says is the main reason - the builder forces you to properly initialize all variables, the fluent api does not, `Vehicle vehicle = new Vehicle();` should not be possible. And all your `final int`s cannot be final in the second snippet, one more reason to not do this because there should not be setters in the first place because a vehicle should not be mutable. – luk2302 Jun 19 '21 at 09:29
  • @luk2302 I will double check my builder with lombok and edit the example code. – Truong Jun 19 '21 at 09:32
  • The *Builder Patter* is an implementation of a *fluent API*. They do not compete. – Timothy Truckle Jun 19 '21 at 10:39
  • @TimothyTruckle we then compare a straight forward implementation of fluent API with a builder pattern approach. – Truong Jun 19 '21 at 11:14
  • does still make no sense. Just use a *fluent API* on the *builder*. Problem solved... – Timothy Truckle Jun 19 '21 at 13:15
  • 1
    @TimothyTruckle You didn't look at the code. The question is somehow hard to describe without looking at every single line of the code snippets. – Truong Jun 19 '21 at 13:19

1 Answers1

5

Some benefits builder pattern compared with fluent api:

  1. Builder can return not only Vehicle class but SomeClass extends Vehicle
  2. You can add custom complex logic into build method (validation, switch implementation etc)
  3. You can't create Vehicle without necessary properties

But it's absolutely normal use your approach if you dont need this benefits. Some libraries use that

User9123
  • 1,643
  • 1
  • 8
  • 21
  • 2
    The builder pattern is also useful because it allows the built objects to be immutable, which can be useful to help ensure overall program correctness. – Chris Gillum Apr 07 '22 at 20:48