2. 当构造方法参数过多时使用 builder 模式

This commit is contained in:
sjsdfg 2019-04-05 16:50:30 +08:00
parent d4a7bf829b
commit e5fec3ce9b

View File

@ -53,11 +53,11 @@ public class NutritionFacts {
NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27);
```
  通常情况下,这个构造方法的调用需要许多你不想设置的参数,但是你不得不为它们传递一个值。 在这种情况下,我们为 `fat` 属性传递了 0 值。 『只有』六个参数可能看起来并不那么糟糕,但随着参数数量的增加,它会很快失控。
  通常情况下,这个构造方法的调用需要许多你不想设置的参数,但是你不得不为它们传递一个值。 在这种情况下,我们为 `fat` 属性传递了 0 值。「只有」六个参数可能看起来并不那么糟糕,但随着参数数量的增加,它会很快失控。
  简而言之,可伸缩构造方法模式是有效的,但是当有很多参数时,很难编写客户端代码,而且很难读懂它。读者不知道这些值是什么意思,并且必须仔细地计算参数才能找到答案。一长串相同类型的参数可能会导致一些细微的 bug。如果客户端意外地反转了两个这样的参数编译器并不会抱怨但是程序在运行时会出现错误行为 (条目 51)。
当在构造方法中遇到许多可选参数时,另一种选择是 JavaBeans 模式,在这种模式中,调用一个无参数的构造函数来创建对象,然后调用 `setter` 方法来设置每个必需的参数和可选参数:
  当在构造方法中遇到许多可选参数时,另一种选择是 JavaBeans 模式,在这种模式中,调用一个无参数的构造函数来创建对象,然后调用 `setter` 方法来设置每个必需的参数和可选参数:
```java
// JavaBeans Pattern - allows inconsistency, mandates mutability
@ -94,9 +94,9 @@ cocaCola.setSodium(35);
cocaCola.setCarbohydrate(27);
```
  不幸的是JavaBeans 模式本身有严重的缺陷。由于构造方法在多次调用中被分割,所以在构造过程中 JavaBean 可能处于不一致的状态。该类没有通过检查构造参数参数的有效性来执行一致性的选项。在不一致的状态下尝试使用对象可能会导致与包含 bug 的代码大相径庭的错误因此很难调试。一个相关的缺点是JavaBeans 模式排除了让类不可变的可能性 (条目 17),并且需要在程序员的部分增加工作以确保线程安全。
  不幸的是JavaBeans 模式本身有严重的缺陷。由于构造方法在多次调用中被分割,所以在构造过程中 JavaBean 可能处于不一致的状态。该类没有通过检查构造参数参数的有效性来执行一致性的选项。在不一致的状态下尝试使用对象可能会导致与包含 bug 的代码大相径庭的错误因此很难调试。一个相关的缺点是JavaBeans 模式排除了让类不可变的可能性(条目 17,并且需要在程序员的部分增加工作以确保线程安全。
  当它的构造完成时,手动“冻结”对象,并且不允许它在解冻之前使用,可以减少这些缺点,但是这种变体在实践中很难使用并且很少使用。 而且,在运行时会导致错误,因为编译器无法确保程序员在使用对象之前调用 `freeze` 方法。
  通过在对象构建完成时手动「冻结」对象,并且不允许它在解冻之前使用,可以减少这些缺点,但是这种变体在实践中很难使用并且很少使用。 而且,在运行时会导致错误,因为编译器无法确保程序员在使用对象之前调用 `freeze` 方法。
  幸运的是,还有第三种选择,它结合了可伸缩构造方法模式的安全性和 JavaBean 模式的可读性。 它是 Builder 模式[Gamma95] 的一种形式。客户端不直接调用所需的对象,而是调用构造方法 (或静态工厂),并使用所有必需的参数,并获得一个 builder 对象。然后,客户端调用 builder 对象的 `setter` 相似方法来设置每个可选参数。最后,客户端调用一个无参的 `build` 方法来生成对象该对象通常是不可变的。Builder 通常是它所构建的类的一个静态成员类 (条目 24)。以下是它在实践中的示例:
@ -269,7 +269,7 @@ public class Calzone extends Pizza {
  请注意,每个子类 builder 中的 `build` 方法被声明为返回正确的子类:`NyPizza.Builder` 的 `build` 方法返回 `NyPizza`,而 `Calzone.Builder` 中的 `build` 方法返回 `Calzone`。 这种技术,其一个子类的方法被声明为返回在超类中声明的返回类型的子类型,称为协变返回类型 ( covariant return typing)。 它允许客户端使用这些 builder而不需要强制转换。
  这些“分层 builder”的客户端代码基本上与简单的 `NutritionFacts` builder 的代码相同。为了简洁起见,下面显示的示例客户端代码假设枚举常量的静态导入:
  这些「分层 builder」的客户端代码基本上与简单的 `NutritionFacts` builder 的代码相同。为了简洁起见,下面显示的示例客户端代码假设枚举常量的静态导入:
```java
NyPizza pizza = new NyPizza.Builder(SMALL)