2.遇到多个构造器参数时要考虑用构造器

问题提出

假如一个类有很多域,域中有一些必需参数和大量可选参数,那么静态工厂和构造器需要提供一个只含必要参数的构造器,然后第二个构造器需要有一个可选参数,第三个有两个可选参数,依次类推,最后一个构造器包含所有可选参数。
这样重叠构造器模式可行,但是当有许多参数的时候,客户端代码会很难编写,并且较难阅读。

解决方案

  1. 使用JavaBeans模式
    即调用一个无参构造器来创建对象,然后调用setter方法来设置每个必要的参数和需要的可选参数。
    这样创建实例很容易,并且代码也容易理解阅读。But,构造过程被分到了几个调用中,而不是一下子就完成,这样在构造过程中JavaBean可能处于不一致状态,可能会导致错误!有时候可以通过手工“冻结”对象来解决这一问题,但是实践中比较少用。另外JavaBean使得类不能做成不可变的,这样程序员要付出额外努力确保线程安全。
  2. 使用Builder模式
    这种方法不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器(或静态工厂),得到一个builder对象,然后在builder对象上调用类似于setter的方法来设置每个相关的可选参数,最后利用无参的build方法来生成不可变的对象。这个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 {
        //compulsory paras
        private final int servingSize;
        private final int servings;
        //optional paras - initialized to default values
        private int calories = 0;
        private int fat = 0;
        private int carbohydrate = 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;
        carbohydrate = builder.carbohydrate;
    }
}

builder的setter返回builder本身,这样调用可以链接起来。

NutritionFacts cocaCola = new NutritionFacts.Builder(240,8)
.calories(100)
.sodium(35).build();

利用build方法进行约束条件检验,将参数从builder拷贝到对象中之后,在对象域而不是builder域对他们进行检验。若违反约束条件,build方法应该抛出IllegalStateException,并告知违反哪个约束条件。
另一种检验情况实在builder中的setter方法中检验。
Builder模式也有不足,比如创建构造器会造成额外的开销,并且Builder模式还比重叠构造器模式更加冗长。

总结

如果类的构造器或静态工厂中具有多个参数,特别是大多数参数都是可选的时候,Builder模式是种不错的选择。这样客户端代码将更容易阅读和编写,构造器也比JavaBeans更加安全。

你可能感兴趣的:(2.遇到多个构造器参数时要考虑用构造器)