《Effective java》读书记录-第2条-遇到多个构造器参数时要考虑用构建器

来源:互联网 发布:人体芯片 知乎 编辑:程序博客网 时间:2024/06/05 10:27


public class NutritionFacts {    private final int servingSize;  //(ml)    private final int servings;     //(per container)    private final int calories;     //    private final int fat;          //(g)    private final int sodium;       //(mg)    private final int carbohydrate; //(g)    public NutritionFacts (int servingSize,int servings) {        this(servingSize,servings,0);    }    public NutritionFacts (int servingSize,int servings,int calories) {        this(servingSize,servings,calories,0);    }    public NutritionFacts (int servingSize,int servings,int calories,int fat) {        this(servingSize,servings,calories,fat,0);    }    public NutritionFacts (int servingSize,int servings,int calories,int fat,int sodium) {        this(servingSize,servings,calories,fat,sodium,0);    }    public NutritionFacts (int servingSize,int servings,int calories,int fat,int sodium,int carbohydrate) {        this.servingSize=servingSize;        this.servings=servings;        this.calories=calories;        this.fat=fat;        this.sodium=sodium;        this.carbohydrate=carbohydrate;    }}

例如 :NutritionFacts pork=new NutritionFacts(500,1,10);

当需要创建NutritionFacts类实例的时候,就可以利用参数列表最短的构造器来创建一个实例。但是构造器调用通常会需要很多你根本不想要设置的参数,但是又不得不为它们传值。

重叠构造器模式可行,但是当有许多参数的时候,客户端代码会很难编写,并且仍然较难以阅读。

当有一连串相同类型的参数的时候,会很容易颠倒其中一些参数的顺序,而编译器却不会出错。

所以会通常会想到用JavaBean模式来替代。

public class NutritionFacts {    private  int servingSize=-1;  //(ml)    private  int servings=-1;     //(per container)    private  int calories=0;     //    private  int fat=0;          //(g)    private  int sodium=0;       //(mg)    private  int carbohydrate=0; //(g)    public NutritionFacts () {}    public void setServingSize(int servingSize) {this.servingSize = servingSize;}    public void setServings(int servings) {this.servings = servings;}    public void setCalories(int calories) {this.calories = calories;}    public void setFat(int fat) {this.fat = fat;}    public void setSodium(int sodium) {this.sodium = sodium;}    public void setCarbohydrate(int carbohydrate) {this.carbohydrate = carbohydrate;}}
但是,JavaBean模式自身有着很严重的缺点。因为构造过程被分到了几个调用中,在构造过程中JavaBean可能处于不一致的状态。类无法仅仅通过检验构造器参数的有效性来保证一致性。JavaBean模式还存在另一点不足,JavaBean模式阻止了把类做成不可变的可能(见第15条,后期会加链接),这就需要程序员付出额外的努力来确保它的线程安全。

如何能够既像重叠构造器那样的安全,又能保证像JavaBean模式那么好的可读性?

Builder模式就能满足这两点需求。

public class NutritionFacts {    private  int servingSize=-1;  //(ml)    private  int servings=-1;     //(per container)    private  int calories=0;     //    private  int fat=0;          //(g)    private  int sodium=0;       //(mg)    private  int carbohydrate=0; //(g)    public static  class Builder{        //Required parameters        private final int servingSize;        private final int servings;        //Optional parameters - 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 calories) {            this.calories = calories;            return this;        }        public Builder fat(int fat) {            this.fat = fat;            return this;        }        public Builder carbohydrate(int carbohydrate) {            this.carbohydrate = carbohydrate;            return this;        }        public Builder sodium(int sodium) {            this.sodium = sodium;            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;    }}
NutritionFacts nutritionFacts=new NutritionFacts.Builder(240,8)                .calories(100)                .sodium(35)                .carbohydrate(27).build();

Builder方式让客户端代码很容易编写,也更加易于阅读。

但是Builder模式也有它自身的不足。为了创建对象,必须先创建builder的构建器。在某些十分注重性能的情况下,它可能就是个问题了。

如果类的构造器或者静态工厂中具有多个参数,设计这种类时,Builder模式就是不错的选择,特别是当大多数参数都是可选的时候。

Builder比构建器更便于编写和阅读,比JavaBean更加安全。

0 0