静态工厂和构造器模式: 不能很好地扩展到大量的可选参数。比如用一个类表示包装食品外面显示的营养成分标签。这些标签中有几个域是必需的:每份的含量、每罐的含量以及每份的卡路里。还有超过2 0 个的可选域: 总脂肪量、饱和脂肪量、转化脂肪、胆固醇、纳,等等。大多数产品在某几个可选域中都会有非零的值。 对于这样的类,应该用哪种构造器或者静态工厂来编写呢?程序员一向习惯采用重叠构造器( telescoping cons tructor )模式,在这种模式下,提供的第一个构造器只有必要的参数,第二个构造器有一个可选参数,第三个构造器有两个可选参数,依此类推,最后一个构造器包含所有可选的参数。
随着参数数目增加,它很快就失去了控制;简而言之,重叠构造器模式可行,但是当有许多参数的时候,客户端代码会很难缩写,并且仍然较难以阅读遇到许多可选的构造器参数的时候,还有第二种代替办法,即 JavaBeans 模式 在这种模式下,先调用一个无参构造器来创建对象,然后再调用setter 方法来设置每个必要的参数,以及每个相关的可选参数: 因为构建过程被分到了几个调用中,在构建过程中JavaBean可能处于不一致的状态。类无法仅仅通过验证构造器参数的有效性来保持一致性。试图使用处于不一致状态的对象,将会导致失败。与此相关的另一点不足在于,JavaBeans模式组织了把类做成不可变的可能,这就需要程序员付出额外的努力来确保他的线程安全。
builder模式
既能保证构造器模式那样的安全性,也能保证JavaBeans模式那么好的可读性;
不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器,得到一个builder对象,然后客户端在builder对象上调用类似于setter方法,设置每个相关的可选参数。最后客户端调用无参的build方法生成不可变的对象;builder是它构建的类的静态成员类。
public class NutritionFacts { private final int servingSize; private final int servings; private final int calories; private final int fat; private final int sodium; private final int carbohydrate; // 构造器,静态内部类 public static class Builder { // 必要参数 private final int servingSize; private final int servings; // 可选参数 private int calories = 0; private int fat = 0; private int carbohydrate = 0; private int sodium = 0; public Builder(int servingSize, int servings) { this.servingSize = servingSize; this.servings = servings; } public Builder calories(int val) { calories = val; return this;//返回Builder类对象本身,以便把调用链接起来 } public Builder fat(int val) { fat = val; return this; } public Builder carbohydrate(int val) { carbohydrate = val; return this; } public Builder sodium(int val) { sodium = val; return this; } public NutritionFacts build() { return new NutritionFacts(this); } } private NutritionFacts(Builder builder) { servingSize = builder.servingSize; servings = builder.servings; calories = builder.calories; fat = builder.fat; sodium = builder.sodium; carbohydrate = builder.carbohydrate; } @Override public String toString() { return "[" + "servingSize:" + servingSize + ",servings:" + servings + ",calories:" + calories + ",fat:" + fat + ",sodium:" + sodium + ",carbohydrate:" + carbohydrate + "]"; } public static void main(String[] args) { NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8) .calories(100).sodium(35).carbohydrate(27).build(); System.out.println(cocaCola); } }builder像个构造器一样,可以对其参数强加约束条件,build()方法可以检验这些约束条件,将参数从builder拷贝到对象之后,在对象域进行检验。 与构造器相比,builder优势在于:可以有多个可变参数。 Java中传统的抽象工厂实现Class对象。用newInstance方法充当build方法的一部分,隐含着问题:newInstance总是试图调用类的无参构造器,若是不存在也不会收到编译错误。客户端必须在运行时处理异常,很不方便。 Class.newInstance破坏了编译时的异常检查,builder接口弥补了不足;
builder模式的不足:为了创建对象必须先创建它的构造器。