当我们看帮助文档的时候,有没有想过它是怎么设计构造器的?为什么这么设置?这样设计有什么好处?
第一种情况:重叠构造器(telescoping constructor)
package com.wisdom.effective.java;
/**
*
* @author Wisdom 第2条:遇到多个构造器参数时要考虑用构造器
*/
public class TelescopingConstructin {
public static void main(String[] args) {
NutritionFacts nf1 = new NutritionFacts(1, 1);
NutritionFacts nf2 = new NutritionFacts(1, 1, 1);
NutritionFacts nf3 = new NutritionFacts(1, 1, 1, 1);
NutritionFacts nf4 = new NutritionFacts(1, 1, 1, 1, 1);
NutritionFacts nf5 = new NutritionFacts(1, 1, 1, 1, 1, 1);
System.out.println(nf1);
System.out.println(nf2);
System.out.println(nf3);
System.out.println(nf4);
System.out.println(nf5);
}
}
class NutritionFacts {
private int servingSize; // (ml) required 份量
private int servings; // (ml) required 份
private int calories; // optional 卡路里
private int fat; // (g) optional 脂肪
private int sodium; // (mg) optional 钠
private int carbohydrate; // (g) optional 糖类
public NutritionFacts(int servingSize, int servings) {
this(servingSize, servings, 0);
}
public void setCarbohydrate(int carbohydrate) {
this.carbohydrate = carbohydrate;
}
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 sadium) {
this(servingSize, servings, calories, fat, sadium, 0);
}
public NutritionFacts(int servingSize, int servings, int calories, int fat, int sadium, int carbohydrate) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sadium;
this.carbohydrate = carbohydrate;
}
}
缺陷:重叠构造器模式可行,但是当有许多参数的时候,客户端会很难编写;如果客户端不小心点到了其中两个参数的顺序,编译器也不会出错,但是程序在运行时会出现错误的行为。
第二种情况:JavaBeans模式
package com.wisdom.effective.java;
/**
*
* @author Wisdom Effective Java中文版 第2条:遇到多个构造器参数时要考虑用构造器
*/
public class TelescopingConstructin_2 {
public static void main(String[] args) {
NutritionFacts_2 nf1 = new NutritionFacts_2();
nf1.setServingSize(240);
nf1.setServings(8);
nf1.setCalories(100);
nf1.setSodium(35);
nf1.setCarbohydrate(27);
System.out.println(nf1);
}
}
class NutritionFacts_2 {
private int servingSize = -1; // (ml) required 份量
private int servings = -1; // (ml) required 份
private int calories = 0; // optional 卡路里
private int fat = 0; // (g) optional 脂肪
private int sodium = 0; // (mg) optional 钠
private int carbohydrate = 0; // (g) optional 糖类
public NutritionFacts_2() {
}
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;
}
}
缺陷:1、因为构造过程被分为几个调用中,在构造过程中JavaBeans可能处于不一致的状态。2、JavaBeans模式组织了把类做成不可变的可能,这就需要程序猿付出额外的努力来确保他的线程安全。
第三种情况:Builder模式(即能保证像重叠构造器那样的安全性,也能保证像JavaBeans模式那么好的可读性),他不直接生成想要的对象,而是让客户端利用所有必要的参数调用类似于setter的方法,来设置每个相关的可选参数。最后,客户端调用无参的build方法来生成不可变的对象。这个builder是它构建的类的静态成员。
package com.wisdom.effective.java;
/**
*
* @author Wisdom Effective Java中文版 第2条:遇到多个构造器参数时要考虑用构造器
*/
public class TelescopingConstructin_3 {
public static void main(String[] args) {
NutritionFacts_3 cocaCola = new NutritionFacts_3.Builder(240, 8)
.calories(100).sodium(35).carbohydrate(27).build();
System.out.println(cocaCola);
}
}
class NutritionFacts_3 {
private int servingSize; // (ml) required 份量
private int servings; // (ml) required 份
private int calories; // optional 卡路里
private int fat; // (g) optional 脂肪
private int sodium; // (mg) optional 钠
private int carbohydrate; // (g) optional 糖类
public static class Builder {
// Required
private int servingSize;
private int servings;
// Optional
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 sodium(int val) {
sodium = val;
return this;
}
public Builder carbohydrate(int val) {
carbohydrate = val;
return this;
}
public NutritionFacts_3 build() {
return new NutritionFacts_3(this);
}
}
private NutritionFacts_3(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}
好处:客户端代码很容易编写,更为重要的是易于阅读。builder模式模拟了具名的可选参数,就像Ada和Python中的一样。
优化版:
package com.wisdom.effective.java;
/**
*
* @author Wisdom Effective Java中文版 第2条:遇到多个构造器参数时要考虑用构造器
*/
public class TelescopingConstructin_3 {
public static void main(String[] args) {
NutritionFacts_3 cocaCola = new NutritionFacts_3.Builder(240, 8)
.calories(100).sodium(35).carbohydrate(27).builder();
System.out.println(cocaCola);
}
}
interface BuilderInter<T>{
public T builder();
}
class NutritionFacts_3 {
private int servingSize; // (ml) required 份量
private int servings; // (ml) required 份
private int calories; // optional 卡路里
private int fat; // (g) optional 脂肪
private int sodium; // (mg) optional 钠
private int carbohydrate; // (g) optional 糖类
public static class Builder implements BuilderInter<NutritionFacts_3>{
// Required
private int servingSize;
private int servings;
// Optional
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 sodium(int val) {
sodium = val;
return this;
}
public Builder carbohydrate(int val) {
carbohydrate = val;
return this;
}
// public NutritionFacts_3 build() {
// return new NutritionFacts_3(this);
// }
public NutritionFacts_3 builder() {
return new NutritionFacts_3(this);
}
}
private NutritionFacts_3(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}