本文定位于理解和总结<Effective Java>的所讲内容,而不是翻译,因此不当之处,还请广大网友指出。
复杂对象在这里指的是有三个以上(不包括三个)的属性的对象。当一个类具有很多属性时,使用构造器或者静态工厂的方式创建新对象时不可避免的需要对很多属性进行赋值,可维护性与可读性都很低。
通常使用重叠构造器(telescoping constructor)或者JavaBean模式来构建新对象(两种模式具体代码实例可参见<Effective Java>),可以提高代码可维护性和可读性,然而这两种方法并不完美。
重叠构造器模式
重叠构造器模式是指类有一系列的构造器,构造器的入参从仅包含必选参数依次递增到包含所有参数,且必选参数构造器通过逐级调用后续构造器来实现。
实际上,重叠构造器模式并没有解决任何问题,当可变参数增多时,客户端代码依然难写又难读,并且扩展性极差,每增加一个属性都要修改很多级的构造器。
JavaBean模式
JavaBean模式是指先通过无参的构造器来创建对象,然后通过setter填充属性的方法来构建新对象。
相对于重叠构造器模式来说,JavaBean模式可读性,可维护性,可扩展性都很高,但是JavaBean模式本身存在严重的缺陷。
对象在构建过程中存在不一致性的可能
由于对象的构建分多次调用才能完成,在构建完成之前,使用对象可能会引起运行时错误,编译期无法察觉。对象是可变的
由于使用setter来构建对象,导致对象本身就失去了成为不可变对象的可能。需要依赖程序员来保证线程安全
不一致性导致需要程序员额外的工作来保证线程安全。
构建者模式
除了以上两种方式以外,还可以使用构建者模式来创建对象,构建者模式通过吸收必选参数和链式配置可选参数来完成对象的构建。一般情况下,构建者模式可实现为构建对象的内部类。如下:
// Builder Pattern
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 {
// Required parameters
private final int servingSize;
private final int servings;
// Optional
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;
}
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;
}
}
在创建对象之前,需要先创建该对象构建者的对象,并提供必选参数,随后链式配置可选参数,调用构建方法来完成对象的构建,如下:
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build();
构建者模式融合了以上两种方式的优点,同时也避免了缺点,既保证了可读性和可维护性又提高了线程安全。
因此当遇到有三个以上属性的复杂对象构建,尤其是有一些属性是可选属性时,不妨考虑使用构建者模式。