【问题标题】:What's the revised builder pattern?修改后的构建器模式是什么?
【发布时间】:2011-02-01 07:00:13
【问题描述】:

GoF 的原始 Builder 模式与 Joshua Bloch 的“修订版 GoF Builder 模式”有什么区别?

【问题讨论】:

    标签: java design-patterns


    【解决方案1】:

    GoF 模式专注于抽象构造步骤,以便通过改变构建器可以获得不同的结果,而“修订的构建器”则针对多个构造器添加的不必要的复杂性问题。所以 GoF 模式更多的是关于抽象,而修改后的模式更多的是关于简单性 (IMO)。

    看看http://en.wikipedia.org/wiki/Builder_patternhttp://rwhansen.blogspot.com/2007/07/theres-builder-pattern-that-joshua.html中的例子应该就很清楚了。

    【讨论】:

      【解决方案2】:

      注意Mikko's answerHansen's example 有一些问题 - 或者,至少与 Bloch 的版本不同,尽管我认为 Bloch 的版本更好。

      具体来说:

      首先,Widget 的字段设置在 Builder.build() 而不是 Widget 构造函数中,因此不是(也不能是)finalWidget 据说是不可变的,但没有什么可以阻止其他程序员加入并在以后添加 setter。

      其次,Hansen 的构建方法中的 cmets 说“这里是预创建验证”。 Bloch (EJ 2ed. p.15) 说:

      在将参数从构建器复制到对象后检查 [不变量] 至关重要,并且在对象字段而不是构建器字段上检查它们(第 39 条)。

      如果您翻到第 39 条(第 185 页),您会看到原因:

      [This] 在检查参数和复制参数之间的“漏洞窗口”期间保护类免受来自另一个线程的参数更改。

      Widget 中的字段是不可变的,不需要任何防御性复制,但是坚持正确的模式会更安全,以防有人出现并添加 Date 或数组或一些可变的 @ 987654332@ 稍后。 (它还可以防止另一个线程在调用build() 的过程中修改Builder,但这是一个非常狭窄的安全窗口,因此最好确保Builders 不在线程之间共享。 )

      更类似于 Bloch 的版本是:

      public class Widget {
          public static class Builder {
              private String name;
              private String model;
              private String serialNumber;
              private double price;
              private String manufacturer;
      
              public Builder( String name, double price ) {
                  this.name = name;
                  this.price = price;
              }
      
              public Widget build() {
                  Widget result = new Widget(this);
      
                  // *Post*-creation validation here
      
                  return result;
              }
      
              public Builder manufacturer( String value ) {
                  this.manufacturer = value;
                  return this;
              }
      
              public Builder serialNumber( String value ) {
                  this.serialNumber = value;
                  return this;
              }
      
              public Builder model( String value ) {
                  this.model = value;
                  return this;
              }
          }
      
          private final String name;
          private final String model;
          private final String serialNumber;
          private final double price;
          private final String manufacturer;
      
          /**
           * Creates an immutable widget instance.
           */
          private Widget( Builder b ) {
              this.name = b.name;
              this.price = b.price;
              this.model = b.model;
              this.serialNumber = b.serialNumber;
              this.manufacturer = b.manufacturer;
          }
      
          // ... etc. ...
      }
      

      所有Widget 字段现在都是final,并且在构造后都经过验证。

      【讨论】:

      • 您的示例是否允许创建一些具有虚假(或丢失、不稳定)属性的 Builder 实例,然后调用“new Widget (myBogusBuilder)”并最终得到一个“不稳定”的 Widget 实例? (例如,不验证价格必须至少大于“1”,因为您只会在 build() 方法中检查)
      • 如果 Widget 构造函数是公共的,但它是私有的。调用它的唯一方法(从类外部)是通过 build()。
      • 哦,真的;没有注意到(要检查我的眼镜......)。谢谢:)
      • 这种方法的问题是您需要定义所有成员两次,一次在构建器中,一次在实际类中。 stackoverflow.com/a/1632099/131929 展示了如何避免这种情况。但是,如果使用另一种方法,尽管对象本身仍然是不可变的,但您不能拥有最终成员。
      猜你喜欢
      • 1970-01-01
      • 2011-06-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-24
      • 1970-01-01
      • 2011-02-24
      • 2017-04-24
      相关资源
      最近更新 更多