解读:如果一个类的构造器或者静态工厂具有多个参数(特别是存在很多可选参数的时候),则应该考虑提供构建器(Builder)实例化该类。
正如书中的例子,如果有多个参数,其中有一些是可选的参数。
1、方式一:提供一个无参构造函数,获取实例,然后通过不断地调用set方法去设置参数,这种方式的坏处就是:构造过程分成了很多步骤,可能导致bean处于不一致的状态(这点怎么理解?)。另外一点这使得类无法做成不可变类了(简单的理解就是不可变类的参数都是在实例化的时候赋值,后续不能再被修改,无法提供set方法)。
2、方式二:提供重叠模式的构造函数(说白了,就是提供n多个构造函数,之间有一点的调用关系)。这种方式如果参数很多,代码很难编写维护,参数太多必定会导致构造函数不知道具体第几个参数是干嘛的,也很难阅读。
3、方式三:提供构建器。也就是本文要说的,直接上代码。
// Builder Pattern - Pages 14-15
package org.effectivejava.examples.chapter02.item02.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 {
// Required parameters
private final int servingSize;
private final int servings;
// Optional parameters - initialized to default values
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;
}
public static void main(String[] args) {
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)
.calories(100).sodium(35).carbohydrate(27).build();
}
}
这种代码似曾相识,例如Lucene的BooleanQuery对象就是利用这种模式(阅读源码慢慢体会)。
这种方式有一个很明显的特点就是利用内部静态类提前准备好外围类需要的参数,然后构建外围类的实例,这样就弥补了第一种方法的一致性和类的不可变性。
对于缺点可能就是每次获取类的实例都必须先创建它的构建器。
因此如果参数很少可以不用考虑构建器,如果一个类后续的扩展可能实例都需要多个参数,前期最好考虑做成构建器的形式进行实例化。
注:关于构建器尝试在自己编写类的过程中多去理解,以及阅读源码多注意下别人是怎么用的,争取自己能灵活运用到实际的开发当中,构建器的代码后面还需要多看多理解、。