1、创建对象的方式
静态工厂和构造器模式:
不能很好地扩展到大量的可选参数。比如用一个类表示包装食品外面显示的营养成分标签。这些标签中有几个域是必需的:每份的含量、每罐的含量以及每份的卡路里。还有超过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模式的不足:为了创建对象必须先创建它的构造器。