Effective Java 2.当面临多个参数的构造器时考虑使用构建器

100 阅读3分钟

静态工厂和构造器共同的局限性,就是都不能很好的扩展到大量的可选参数。有一些场景中的类字段一部分字段是必须的,一部分字段是可选的,这种场景很常见。

public class Cat {
    private Integer id;// id 必要字段
    private String name;// 名称 必要字段
    private Integer age;// 年龄 必要字段
    private BigDecimal price;// 价格 必要字段
    private Double height;// 身高 可选字段
    private Double weight;// 体重 可选字段

    public Cat() {
    }
  1. 很常见的操作就是使用重叠构造器的方式,提供一个只有必要参数的构造器,其他构造器做另外的定制化。简单来说,重叠构造器的方式看似可行,但是如果参数过多的情况下。不仅写比较麻烦,别人读也比较难受。如果看代码的人想要知道这些值是什么意思,需要根据顺序数第几个参数是什么意思,如果有很多相同类型的参数,就很容易出错。
public class Cat {
    private Integer id;// id 必要字段
    private String name;// 名称 必要字段
    private Integer age;// 年龄 必要字段
    private BigDecimal price;// 价格 必要字段
    private Double height;// 身高 可选字段
    private Double weight;// 体重 可选字段

    public Cat() {
    }

    /**
     * 必要参数构造器
     */
    public Cat(Integer id, String name, Integer age, BigDecimal price) {
        this(id, name, age, price, null, null);
    }

    /**
     * 除了必要参数增加一个身高字段等
     */
    public Cat(Integer id, String name, Integer age, BigDecimal price, Double height) {
        this(id,name,age,price,height,null);
    }

    public Cat(Integer id, String name, Integer age, BigDecimal price, Double height, Double weight) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.price = price;
        this.height = height;
        this.weight = weight;
    }
}
  1. 还有另一种方式就是通过setter方法来将每个必要的参数和可选的参数进行赋值。弥补了重叠构造器的不足,并且代码阅读起来也容易一些。缺点就是:构造过程被分成了几个方法中,在set的过程中可能处于不一致的状态。类无法仅仅通过检验构造器参数的有效性来保证一致性(可能这一点没有理解的很清楚,看不太懂!)
  2. 通过建造者模式来创建对象,既能保证像重叠构造器模式的安全性(链式编程,一次调用多个方法),也能保证像Setter方法那样好的可读性。并且在build的过程中可以增加必要参数是否赋值的校验来做定制化操作 如果类的构造器或者静态工厂中有多个参数,Builder模式是不错的选择
public class Cat {
    private Integer id;// id 必要字段
    private String name;// 名称 必要字段
    private Integer age;// 年龄 必要字段
    private BigDecimal price;// 价格 必要字段
    private Double height;// 身高 可选字段
    private Double weight;// 体重 可选字段

    public static class CatBuilder{
        private Integer id;// id 必要字段
        private String name;// 名称 必要字段
        private Integer age;// 年龄 必要字段
        private BigDecimal price;// 价格 必要字段
        private Double height;// 身高 可选字段
        private Double weight;// 体重 可选字段

        public CatBuilder id(Integer id){
            this.id = id;
            return this;
        }

        public CatBuilder name(String name){
            this.name = name;
            return this;
        }

        public CatBuilder age(Integer age){
            this.age = age;
            return this;
        }

        public CatBuilder price(BigDecimal price){
            this.price = price;
            return this;
        }

        public CatBuilder height(Double height){
            this.height = height;
            return this;
        }

        public CatBuilder weight(Double weight){
            this.weight = weight;
            return this;
        }

        public Cat build(){
            if(Objects.isNull(id)){
                throw new IllegalArgumentException("CatBuild.id is not null! ");
            }
            if(Objects.isNull(name)){
                throw new IllegalArgumentException("CatBuild.name is not null! ");
            }
            if(Objects.isNull(age)){
                throw new IllegalArgumentException("CatBuild.age is not null! ");
            }
            if(Objects.isNull(price)){
                throw new IllegalArgumentException("CatBuild.price is not null! ");
            }
            return new Cat(this);
        }
    }
    public Cat(CatBuilder builder) {
        id = builder.id;
        name = builder.name;
        age = builder.age;
        price = builder.price;
        height = builder.height;
        weight = builder.weight;
    }
}