第二条:遇到多个构造器参数时要考虑使用构建器
以下内容基本上来自《Effective Java》,该书讲的非常精辟,因此大部分解释直接来源书中原句。
一.场景
当遇到某个类有许多参数时,其中含有许多可选的参数,对于这种的类,应该用哪种构造器或静态工厂来编写呢?
二.问题解决
讨论
这时我们如果使用【重叠构造器模式】编写这种的类,当我们在给我们想传递的参数传值时,有时会遇到问题,
假设类中有4个int型参数(a,b,c,d),重叠构造器提供4种构造器(a),(a,b),(a,b,c),(a,b,c,d),这时倘若我们想对a,d传值,那么我们需要给所有的参数传值。
另外客户端代码会难以阅读,当参数数目增多时,你必须仔细地数着这些参数。
当然,这里只有4个参数,问题随着参数数目增加,【重叠构造器模式】会更加不方便。
我们这里提出【构造者模式】
优势
它既能保证像重叠构造器模式那样的安全性,也可以保证像JavaBeans模式那么好的可读性。
它不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器(或者静态工厂),得到一个builder对象。然后客户端在builder对象上调用类似setter的方法,来设置每个相关的可选参数。
最后,客户端调用无参的build方法来生成通常是不可变的对象。
示例
//建造者模式(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 {
// 必要的参数
private final int servingSize;
private final int servings;
// 可选参数
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
// 让客户端利用所有必要的参数调用构造器(或者静态工厂),得到一个builder对象
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;
}
// 创建NutritionFacts对象
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
// builder方法给对象设值
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[]) {
// 创建对象且设置必要的参数servingSize与servings的值,并且可以选择对另外四个可选参数也进行设值
NutritionFacts cocola = new NutritionFacts.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build();
System.out.println(cocola.servingSize);
System.out.println(cocola.calories);
System.out.println(cocola.fat);// fat没设值,所以返回的是它初始值
}
} Builder模式也适用与类层次结构import java.util.*;
//Builder pattern for class hierachie
public abstract class Pizza {
public enum Topping { HAM, MUSHROOM, ONION, PEPPER, SAUSAGE }
final Set<Topping> toppings;
abstract static class Builder<T extends Builder<T>> { //创建Builder类
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
}
} 总结
简而言之,如果类的构造器或者静态工厂中具有多个参数,设计这种类时,Builder模式就是一种不错的选择,特别是当大多数参数都是可选或者类型相同的时候。与使用重叠构造器模式相比,使用Builder模式的客户端代码将更易于阅读和编写,构建器也比JavaBeans更加安全。


京公网安备 11010502036488号