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

本文介绍了如何使用Builder模式解决NutritionFacts类的构造问题,提供了一种既安全又易于阅读的解决方案,同时讨论了Builder模式的优缺点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


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更加安全。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值