第二条:遇到多个构造器参数时要考虑使用构建器
以下内容基本上来自《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更加安全。