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

2019-06-17 08:24发布

问题:

I had a question regarding the "Builder Pattern" covered in "Effective Java". Do we need a .build() method for it to correctly implement the pattern? For instance, let's say that we have the following class:

public class CoffeeDrink {

    private int numEspressoShots;
    private short milkType;
    private boolean withWhip;

    private CoffeeDrink() {
    }

    public static CoffeeDrink buildNewDrink() {
        return new CoffeeDrink();
    }

    public CoffeeDrink withEspresso(int n) {
        this.numEspressoShots = n;
        return this;
    }

    public CoffeeDrink withMilkType(shot t) {
        this.milkType = t;
        return this;
    }

    public CoffeeDrink withWhip() {
        this.withWhip = true;
        return this;
    }
}

And then how we use it:

CoffeeDrink c = CoffeeDrink.buildNewDrink()
                         .withEspresso(2)
                         .withMilkType(2)
                         .withWhip();

Would this still be valid if I don't have a static inner Builder class? I guess that one of the advantages is that it holds off from creating a new CoffeeDrink object until the method .build() is called, but I'm still creating a Builder object. Just seeking some clarifications.

回答1:

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.



回答2:

According to the GoF reference, build() isn't required. The original reference doesn't use chaining, and there is a getResult() step at the end of the Director.construct() method. The Director class takes care of encapsulating the build process, so Clients don't need to worry if they're building things correctly. It's the responsibility of the Director.

Here's the sequence diagram from the GoF reference on Builder: