目录

重叠构造器

JavaBeans

Builder模式


        建设现在有一个实际问题:使用一个类表示包装食品外面显示的各种营养标签。这些标签中有几个属性是必须:每份的含量和每份的卡路里。还有超过20个可选的属性:总脂肪量、钠含量、蛋白质含量等等。大多数产品在某几个可选的属性中会有非零的值。对于这样的类,如何构建呢?

重叠构造器

        如下我们使用很多个构造器实现了该类:

/**
 * Created by leboop on 2020/5/24.
 */
public class NutritionFacts {
    // 必须属性
    private int servingSize;
    private int calories;

    // 可选属性(超过20个,为了方便使用2个)
    private int fat;
    private int sodium;

    // 无参构造器
    public NutritionFacts() {
    }

    // 必要参数构造器
    public NutritionFacts(int servingSize, int calories) {
        this.servingSize = servingSize;
        this.calories = calories;
    }

    public NutritionFacts(int servingSize, int calories, int fat) {
        this(servingSize, calories,fat,0);
    }

    // 全部参数构造器
    public NutritionFacts(int servingSize, int calories, int fat, int sodium) {
        this.servingSize = servingSize;
        this.calories = calories;
        this.fat = fat;
        this.sodium = sodium;
    }
}

上面可选参数,我们仅仅使用了2个,如果是20个,构造器复杂度可想而知。客户端代码如下构造对象:

/**
 * Created by leboop on 2020/5/24.
 */
public class BuilderMain {
    public static void main(String[] args) {
        NutritionFacts nutritionFacts=
                new NutritionFacts(20,30,20,12);
    }
}

重叠构造器模式有如下几个特点:

(1)重叠构造器模式是可行的;

(2)参数过多时,客户端代码编写困难;

(3)客户端代码可读性较差;

 

        下面尝试使用JavaBeans模式对重叠构造器模式进行改进。

JavaBeans

        JavaBeans模式通过先构造一个无参构造器,然后再调用setter方法来设置每个参数,具体代码如下:

/**
 * Created by leboop on 2020/5/24.
 */
public class NutritionFactsBeans {
    // 必须属性
    private int servingSize;
    private int calories;

    // 可选属性(超过20个,为了方便使用2个)
    private int fat;
    private int sodium;

    // 无参构造器
    public NutritionFactsBeans() {
    }

    public void setServingSize(int servingSize) {
        this.servingSize = servingSize;
    }

    public void setCalories(int calories) {
        this.calories = calories;
    }

    public void setFat(int fat) {
        this.fat = fat;
    }

    public void setSodium(int sodium) {
        this.sodium = sodium;
    }
}

此时客户端就可以如下构造对象了:

/**
 * Created by leboop on 2020/5/24.
 */
public class NutritionFactsBeansMain {
    public static void main(String[] args) {
        NutritionFactsBeans bean=new NutritionFactsBeans();
        bean.setServingSize(20);
        bean.setCalories(30);
        bean.setFat(30);
    }
}

JavaBeans模式有如下几个特点:

(1)相对重叠构造器模式,客户端代码可读性有增强,创建实例也相对容易些;

(2)JavaBeans模式有一个严重的缺点,就是对象的构造分解为多个setter方法调用中,在构造过程中,对象可能处于不一致的状态,比如多线程访问该对象时,主线程在调用完bean.setCalories(30);另一个线程访问了该对象,此时得到的fat属性值为0,而主线程接着调用完bean.setFat(30);得到的对象的fat属性值就变为30了。

 

        下面使用Builder模式解决如上模式的缺点。

Builder模式

        Builder模式有很多的写法,这里采用如下写法:

/**
 * Created by leboop on 2020/5/24.
 */
public class NutritionFactsBuilder {
    // 必须属性
    private int servingSize;
    private int calories;

    // 可选属性(超过20个,为了方便使用2个)
    private int fat;
    private int sodium;

    // 无参构造器
    public NutritionFactsBuilder(Builder builder) {
        this.servingSize = builder.servingSize;
        this.calories = builder.calories;
        this.fat = builder.fat;
        this.sodium = builder.sodium;
    }

    public static class Builder {
        // 必须属性
        private int servingSize;
        private int calories;

        // 可选属性(超过20个,为了方便使用2个)
        private int fat;
        private int sodium;

        public Builder(int servingSize, int calories) {
            this.servingSize = servingSize;
            this.calories = calories;
        }

        public Builder builderFat(int fat) {
            this.fat = fat;
            return this;
        }

        public Builder builderSodium(int sodium) {
            this.sodium = sodium;
            return this;
        }

        public NutritionFactsBuilder builder() {
            return new NutritionFactsBuilder(this);
        }

    }
}

描述食品外包显示的营养成分的类有三个部分组成:

(1)属性;

(2)构造全部属性的构造器;

(3)内部类Builder;

类的构造由内部类Builder完成。客户端代码调用如下:

/**
 * Created by leboop on 2020/5/24.
 */
public class NutritionFactsBuilderMain {
    public static void main(String[] args) {
        NutritionFactsBuilder nutritionFacts = new NutritionFactsBuilder
                .Builder(20, 30)
                .builderFat(30)
                .builderSodium(20).builder();
    }
}

Builder模式有如下几个特点:

(1)客户端代码可读性强,编写也相对容易;

(2)对象的构造是安全的,弥补了JavaBeans模式。