第二条:遇到多个构造器参数时要考虑使用构建器

以下内容基本上来自《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更加安全。