静态工厂和构造器有个共同的局限性:它们都不能很好地扩展到大量的可选参数.对于这样的类,应该用那种构造器或者静态方法来写?

第一种方式:采用重叠构造器模式,在这种模式下,你提供第一个只有必要参数的构造器,第二个构造器有一个可选参数,第三个有两个可选参数,以此类推,最后一个构造器包含所有可选参数.

这种模式可行,但是当参数非常多的时候,客户端的代码会很难编写,并且难以阅读.有时候,类型相同的参数还会造成这样的问题:如果客户端不小心颠倒了其中两个参数的顺序,编译器不会报错,但是程序在运行时会出现错误.

第二种方法:是JavaBean模式,这种模式下,调用一个无参构造器来创建对象,然后调用setter方法来设置每个必要的参数,以及每个相关的可选参数:

java 类中有多个构造函数 java多个构造器_客户端


客户端代码:

java 类中有多个构造函数 java多个构造器_构造器_02


优点:

创建实例很容易,代码可读性高.

缺点:

JavaBean模式自身有着很严重的缺点.因为构造过程被分到了几个调用中,在构造过程中JavaBean可能处于不一致的状态.使用处于不一致状态的对象,将会导致失败,并且这种错误调试十分困难.

JavaBean模式阻止类变成不可变的可能(见第15条),这需要程序员来确保线程安全.

可以通过手动”冻结”对象,来弥补这些不足,意思是:在对象构造完成或者解冻之前不允许使用.这种方式在实践中很少使用.

还有第三种方式,既能保证重叠构造器那样的安全性,又能保证JavaBean那样的可读性.这就是Builder模式的一种形式.
这种方式,不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器(或者静态工厂),得到一个builer对象.然后客户端在builder对象上调用类似setter方法,来设置每个相关的可选参数.最后,客户端调用无参的builder方法来生成不可变的对象.例子:

package cn.nieyi.builder;

/**
 * 测试Builder模式
 * @author Administrator
 *
 */

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;

        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 carbohydrate(int val){
            carbohydrate = 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;
    }
}

客户端代码:
package cn.nieyi.builder;

public class Client {
    public static void main(String[] args) {
        NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(0).sodium(35).sodium(35).calories(27).build();
    }
}

builder像个构造器一样,可以对其参数强加约束条件.builder方法可以检验这些约束条件.

将参数从builder拷贝到对象中之后,并在对象域而不是builder域中检验.如果违反了约束条件,builder方法就应该抛出IllegalStateException.异常信息应该显示出违反了那个约束条件.

对多个参数强加约束条件的另一种方法是,用多个setter方法对某个约束条件必须持有的所有参数进行检查.

与构造器相比,builder的微略优势在于,builder可以有多个可变参数.构造器就像方法一样,只能有一个可变参数.而builder利用单独的方法来设置每个参数,你想要多少个可变参数,它们就可以有多少个,直到每个setter方法都有一个可变参数.

Java中传统的抽象工厂实现是Class对象,用newInstance方法充当build方法的一部分.newInstance方法总是企图调用类的无参数构造器.如果类没有可以访问的无参构造器,也不会受到编译时错误.Class.newInstance破坏了编译时的异常检查.

builder模式的不足:
为了创建对象,必须先创建它的构建器.虽然创建构建器的开销在实践中可能不哪些明显,但是在某些十分注重性能的情况下,可能就成问题了.
Builder模式还比重叠构造器模式更加冗长,因此它只在有很多参数的时候才使用,比如4个或者更多个参数.

简而言之,如果类的构造器或者静态工厂中具有多个参数,设计这种类时,Builder模式是种不错的选择,特别是当大多数参数都是可选的时候.与使用传统的重叠构造器模式相比,使用Builder模式的客户端代码将更易于阅读和编写,构建器也比JavaBean更加安全.