一、为什么要引入 Builder 模式
静态工厂和构造器都有个共同的缺陷,不能很好的扩展到大量的可选参数
比如包装食品外面显示的营养成分标签,这些标签中,有几个是必须的,也有很多是可选的
当然,当可选参数比较多时,采用 重叠构造器模式也可以,但是此时代码的可读性不好,也很难编写
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
public NutritionFacts(int servingSize, int servings) {
this(servingSize, servings, 0);
}
public NutritionFacts(int servingSize, int servings, int calories) {
this(servingSize, servings, calories, 0);
}
public NutritionFacts(int servingSize, int servings, int calories, int fat) {
this(servingSize, servings, calories, fat, 0);
}
public NutritionFacts(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;
}
}
所以可以引入Builder 模式,builder 模式可以利用单独的方法设置每一个参数
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
public static class Builder {
// 必须的参数
private final int servingSize;
private final int servings;
// 可选的参数
private int calories = 0;
private int fat = 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 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;
}
}
builder 设置方法返回 builder 本身,可以把调用链接起来,得到一个流式的 API:
NutritionFacts cocaCola = new Builder(240, 8)
.calories(100).sodium(34).build();
二、具体使用
实际开发中,可以借助 lobmok 的@Builder
方法,实现建造者模式
例如:对 lombok 的@Builder
和@Data
组合
@Data
@Builder
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
}
使用:
NutritionFacts cocaCola = NutritionFacts.builder()
.servingSize(240)
.servings(8)
.calories(100)
.sodium(34).build();
Builder 模式也有自身的不足,为了创建对象,必须先创建它的构造器,虽然创建这个构造器的开销不明显,但是在某些注重性能的情况下,可能就成为了缺陷
但是如果一开始就使用构造器或者静态方法,等到类需要多个参数时才添加构造器,就无法控制,那些过时的构造器或者静态工厂显得很不协调,因此一开始最好就使用构建器