Builder Pattern in Effective Java

Builder Pattern in Effective Java

Make the builder a static class. Then it will work. If it is non-static, it would require an instance of its owning class - and the point is not to have an instance of it, and even to forbid making instances without the builder.

public class NutritionFacts {
public static class Builder {
}
}

Reference: Nested classes

Is the Builder pattern in the Effective Java book thread safe?

There is a difference between a static member field (shares memory across all instances of said class), and a static inner class (does not see or share data with outer class).

Effective Java is promoting a pattern that uses the latter approach, which does not expose you to any thread safety issues.

If for whatever reason the same Builder instance were shared across two threads in your program, then yes, you have a thread safety issue.

Effective Java: Builder Pattern

You have not even tried to compile this, because unfortunately it will fail. The reason for this is because you declared a variable final, it must be initialized by the time the constructor is finished.

This is incidentially why setters on final variables make no sense and also why the Builder pattern is often used to work around this problem.

Is a nested Builder class really necessary as described in Effective Java?

Builder is a pattern for the construction of complex objects. I wouldn't count your example as complex; indeed, the builder adds an awful lot of unnecessary code, rather than just using constructor parameters.

There are a few reasons why you'd want to use a builder:

  • To construct complex immutable objects. Immutable objects need to have final (or logically final) fields, so they must be set at construction time.

    Let's say that you have N fields, but you only want to explicitly set some of them in certain use cases. You would need up to 2^N constructors to cover all of the cases - known as "telescoping", since the length of the parameter list gets longer and longer. The builder allows you to model optional parameters: if you don't want to set that parameter, don't call that setter method.

  • To allow self-documentation of the parameters' meaning. By naming the setter methods appropriately, you can see what the values mean at a glance.

    It also helps to verify that you are not accidentally reversing parameters of the same type, since you can see what each value is used for.

Builder pattern validation - Effective Java

Object validation is integral part of object creation using builders. Although you can have a separate routine doing the validation, such separation is not required: validation code could be part of the function doing the build. In other words, you can do this

TargetObject build() {
TargetObject res = new TargetObject();
res.setProperty1();
res.setProperty2();
validate(res); // This call may throw an exception
return res;
}

void validate(TargetObject obj) {
if (...) {
throw new IllegalStateException();
}
}

or this:

TargetObject build() {
TargetObject res = new TargetObject();
res.setProperty1();
res.setProperty2();
if (...) {
throw new IllegalStateException();
}
return res;
}

The important thing is that the validation takes place after, not before, the construction of the target object. In other words, you need to validate the state of the object, not the state of the builder.

Do we need a .build() method in the Builder Pattern?

No, this is not the Builder pattern. It's valid Java, and it will compile and run. But your buildNewDrink() method, whether it's called build() or buildNewDrink() or something else, is just a simple Factory Method that creates a CoffeeDrink. Those other methods are like setter methods that happen to return themselves.

The static nested Builder class is necessary. While holding off on creating the class instance, it can perform validation logic to ensure that an invalid object is not created. I'm not sure that there is an invalid state to a CoffeeDrink as you have it, but if it did, with your code, it would be possible to create a CoffeeDrink and have it in an invalid state after it was created, but before other methods were called. The Builder pattern eliminates this possibility by validating the data before building the instance. It also eliminates the need for constructor explosion, where lots of constructors with all possible combinations of parameters are needed, to cover all possible cases.

Builder Pattern: which variant is preferred?

None of the above.

The first one doesn't allow building an immutable Vehicle, which is often why the Builder pattern is used.

The second example is a variation of the first one which allows getting information from the builder using additional getter methods. But those those methods aren't used anywhere, except in the Vehicle constructor, which has access to the builder fields directly. I don't see the point in adding them.

I see two more important things to improve:

  1. The two builder types do exactly the same thing. There's no need for two types. A single one is sufficient.
  2. What the createVehicle() method does should be done by the builder constructor. If you construct a CarBuilder, it's obviously to build a car, so the type of the vehicle should be set as soon as the builder is constructed. Here's how I would write it:

.

public final class Vehicle {

private final String type;
private final int wheels;

private Vehicle(Builder builder) {
this.type = builder.type;
this.wheels = builder.wheels;
}

public static Builder carBuilder() {
return new Builder("car");
}

public static Builder truckBuilder() {
return new Builder("truck");
}

public static class Builder {
private final String type;
private int wheels;

private Builder(String type) {
this.type = type;
}

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

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

public static void main(String[] args) {
Vehicle car = Vehicle.carBuilder().addWheels(4).build();
Vehicle truck = Vehicle.truckBuilder().addWheels(10).build();
}
}


Related Topics



Leave a reply



Submit