Builder模式是为了应对大量的可选参数的情况。
Builder模式的优势
- 相比于set方法,builder模式可以让所有的属性都是final的,只能在build的时候初始化一次
- 和重载多个构造器相比,代码的可读性很高
- 可以有多个可变参数
- 易于做参数检查和构造约束检查
- 灵活性: 可以利用单个builder构建多个对象
- 可以自动填充某些域, 比如自增序列号
对于Builder模式中必填字段,当必填字段比较多的时候,也可以不把这些字段放到构造器里,在build的时候做一次校验,如果缺失了必填字段则抛Exception。把编译限制退化成运行时的限制。
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 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 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;
}
}
Builder模式在很多框架中都有体现,比如Spring Security
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/templates/*.html").permitAll()
.antMatchers("/templates/member/**").hasRole("memmber")
.antMatchers("/templates/employee/**").hasRole("admin")
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll()
.and()
.logout()
.permitAll();
}
Builder模式继承
Builder模式也可以适用于继承结构,这里就会用到"递归类型参数"的内容。书中也举了一个例子,逻辑有点绕,不过能看懂个大概。
这方面使用场景不多,简单看看就可以,如果真的使用到了也可以直接套作者的例子
public abstract class Pizza {
public enum Topping {HAM, MUSHROOM, ONION, PEPPER, SAUSAGE}
final Set<Topping> toppings;
abstract static class Builder<T extends Builder<T>> {
EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);
public T addTopping(Topping topping) {
toppings.add(Objects.requireNonNull(topping));
return self();
}
abstract Pizza build();
// Subclasses must override this method to return "this"
protected abstract T self();
}
Pizza(Builder<?> builder) {
toppings = builder.toppings.clone(); // See Item 50
}
}
public class NyPizza extends Pizza {
public enum Size {SMALL, MEDIUM, LARGE}
private final Size size;
public static class Builder extends Pizza.Builder<Builder> {
private final Size size;
public Builder(Size size) {
this.size = Objects.requireNonNull(size);
}
@Override
public NyPizza build() {
return new NyPizza(this);
}
@Override
protected Builder self() {
return this;
}
}
private NyPizza(Builder builder) {
super(builder);
size = builder.size;
}
@Override
public String toString() {
return "New York Pizza with " + toppings;
}
}
public class Calzone extends Pizza {
private final boolean sauceInside;
public static class Builder extends Pizza.Builder<Builder> {
private boolean sauceInside = false; // Default
public Builder sauceInside() {
sauceInside = true;
return this;
}
@Override
public Calzone build() {
return new Calzone(this);
}
@Override
protected Builder self() {
return this;
}
}
private Calzone(Builder builder) {
super(builder);
sauceInside = builder.sauceInside;
}
@Override
public String toString() {
return String.format("Calzone with %s and sauce on the %s",
toppings, sauceInside ? "inside" : "outside");
}
}
TMS里使用场景无所不在,大多数VO都有十几、几十个字段,使用builder相对比较合适一点
网友评论