《Effective Java》读书笔记:用Builder模式代替含多个参数的构造器
当一个对象含有多个成员变量且创建对象时需要初始化时,直接使用构造器来初始化会造成使用上的不便。例如
public class BuilderDemo { 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 BuilderDemo(int servingSize, int servings) { this.servingSize = servingSize; this.servings = servings; } public BuilderDemo(int servingSize, int servings, int calories) { this.servingSize = servingSize; this.servings = servings; this.calories = calories; } public BuilderDemo(int servingSize, int servings, int calories, int fat) { this.servingSize = servingSize; this.servings = servings; this.calories = calories; this.fat = fat; } public BuilderDemo(int servingSize, int servings, int calories, int fat, int sodium) { this.servingSize = servingSize; this.servings = servings; this.calories = calories; this.fat = fat; this.sodium = sodium; } public BuilderDemo(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) { this.servingSize = servingSize; this.servings = servings; this.calories = calories; this.fat = fat; this.sodium = sodium; this.carbohydrate = carbohydrate; }
如果只需要后面的可选参数,但调用的时候必须提供前面的可选参数。而且参数很长时会造成阅读和修改上的不便。
另外的一种做法是提供每个成员变量的set方法,在创建对象之后设置成员变量的内容。但因为构造过程被分到了几个调用中,构造过程中对象可能处于不一致的状态。
最理想的方法是使用Builder模式,通过一个内部静态类来完整的构造对象。代码如下:
public class BuilderDemo { 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 sodium = 0; private int carbohydrate = 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 sodium(int val) { sodium = val; return this; } public Builder carbohydrate(int val) { carbohydrate = val; return this; } public BuilderDemo build() { return new BuilderDemo(this); } } private BuilderDemo(BuilderDemo.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) { BuilderDemo demo = new Builder(34, 32).calories(34).carbohydrate(43).build(); } }
这样的代码很容易编写和阅读。而且可以对其参数强加约束条件,build方法可以检验这些约束条件,将参数从builder拷贝到对象中,并在对象域中对它们进行检验。
Builder模式的不足:1.创建对象前需要创建构建器,在十分注重性能的情况下不适用。2.Builder比重叠构造器模式更加冗长,因此只有在很多参数时才使用,比如4个以上。