【问题标题】:Do we need a .build() method in the Builder Pattern?我们需要在 Builder 模式中使用 .build() 方法吗?
【发布时间】:2015-01-16 23:36:43
【问题描述】:

我对“Effective Java”中的“Builder Pattern”有疑问。我们是否需要一个.build() 方法才能正确实现该模式?例如,假设我们有以下类:

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

然后我们如何使用它:

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

如果我没有静态内部 Builder 类,这仍然有效吗?我想其中一个优点是它不会创建一个新的CoffeeDrink 对象,直到调用.build() 方法,但我仍在创建一个Builder 对象。只是寻求一些澄清。

【问题讨论】:

  • 构建器模式在不可变类中发挥了巨大的优势。有了它们,Builder 是一个可变的“助手”类,它帮助最终构建最终产品,该产品此后是不可变的。您的示例只是一个简单的 POJO,它具有 JavaBeans 的缺点(可变,可以在不完整或不一致的状态下查看等)。

标签: java design-patterns builder effective-java


【解决方案1】:

不,这不是 Builder 模式。它是有效的 Java,它将编译和运行。但是您的buildNewDrink() 方法,无论是称为build() 还是buildNewDrink() 或其他名称,都只是创建CoffeeDrink 的简单工厂方法。那些其他方法就像 setter 方法一样,碰巧会返回自己。

static 嵌套的 Builder 类是必需的。在推迟创建类实例时,它可以执行验证逻辑以确保不创建无效对象。我不确定 CoffeeDrink 是否存在无效状态,但如果确实如此,则可以使用您的代码创建一个 CoffeeDrink 并使其处于无效状态之后创建,但在调用其他方法之前。 Builder 模式通过在构建实例之前验证数据来消除这种可能性。它还消除了构造函数爆炸的需要,其中需要大量具有所有可能参数组合的构造函数,以涵盖所有可能的情况。

【讨论】:

  • 我同意你的说法。这些方法似乎是带有 chaining 的常规设置器。只是一个小细节,但您可能会在答案中遗漏 "not",如 ",它可以执行验证逻辑以确保无效对象是 not 创建”.
  • 感谢您的澄清。那么在返回 CoffeeDrink 的实例之前,我会验证 Builder 类的 builder 函数中的所有参数吗?
  • 是的。 build() 方法在链中最后被调用。它验证之前传入的所有数据,并创建并返回所需的实例。
  • GoF(Gamma 等人)的 Builder 模式没有 build() 方法。它确实说 ConcreteBuilders 提供 an interface for retrieving the product,例如,GetMaze() 以返回逐步构建的迷宫。 GoF 中的示例代码都没有使用链接。许多人使用 Builder 与 GoF 不同。他们的想法是让导演封装所有构建步骤。 “验证”的概念不太重要,因为具体构建器不是客户端上的代码,而是由 director 内部的 API 提供者控制的代码。
【解决方案2】:

根据 GoF 参考资料,build() 不是必需的。原始引用没有使用链接,在Director.construct() 方法的末尾有一个getResult() 步骤。 Director 类负责封装构建过程,因此Clients 无需担心它们是否正确构建。这是Director 的责任。

这是来自 Builder 上 GoF 参考的序列图:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-06
    • 2021-04-23
    • 1970-01-01
    • 1970-01-01
    • 2011-11-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多