共计 4191 个字符,预计需要花费 11 分钟才能阅读完成。
前言
上一篇记录了用动态工厂 VS 结构器,本片持续记录书中的提到的其它的形式。
论述
在第二节开篇,作者提出本人的见解。
动态工厂和结构器有个独特的局限性,它们都不能很好的拓展到大量的可选参数。
本想应用本人在练习中的例子,但书中给的实例切实是优良,所以下文大部分援用书中的案例。比方,用一个类示意包装食品的营养成分标签,其中有几个域是必须的:每份的含量、每罐的含量以及每份的卡路里。还有几个可选域:总脂肪量、饱和脂肪量、转化脂肪、胆固醇、钠等等。大多数产品在几个可选域中都会有非零的值。
重叠结构器
对于选用哪种形式来创立,依照咱们惯例的形式个别就会采纳 重叠结构器 模式,这种模式中,提供的第一个结构器只需蕴含必要的参数,第二个结构器有一个可选参数,第二个结构器有两个可选参数,以此类推,最初一个结构器蕴含所有的可选参数。以下是一个只显示四个可选域的实例:
public class NutritionFacts {
private final int servingSize; // (mL) required
private final int servings; // (per container) required
private final int calories; // optional
private final int fat; // (g) optional
private final int sodium; // (mg) optional
private final int carbohydrate; // (g) optional
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;
}
}
public static void main(String[] args) {
NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27);
}
public class NutritionFacts {
// Parameters initialized to default values (if any)
private int servingSize = -1;
private int servings = -1;
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public NutritionFacts() {}
// set function
public void setServingSize(int val) {servingSize = val;}
public void setServings(int val) {servings = val;}
public void setCalories(int val) {calories = val;}
public void setFat(int val) {fat = val;}
public void setSodium(int val) {sodium = val;}
public void setCarbohydrate(int val) {carbohydrate = val;}
}
public static void main(String[] args) {
NutritionFacts cocaCola = new NutritionFacts();
cocaCola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100);
cocaCola.setSodium(35);
cocaCola.setCarbohydrate(27);
}
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
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 val) {
calories = val;
return this;
}
public Builder fat(int val) {
fat = val;
return this;
}
public Builder carbohydrate(int val) {
carbohydrate = val;
return this;
}
public Builder sodium(int val) {
sodium = val;
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;
}
}
public static void main(String[] args) {
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)
.calories(100).sodium(35).carbohydrate(27).build();
}
尽管本书中的规定不会百分之百地实用于任何时刻和任何场合,然而,它们的确体现了绝大多数状况下的最佳编程实际。你不应该自觉的遵循这些规定,但偶然有了充沛的理由之后,能够去突破这些规定。同大多数学科一样,学习编程艺术首先要学会根本的规定,而后能力晓得什么时候去突破它。
十分喜爱的书中摘录:
总结
这样的客户端代码很容易编写,更为重要的是易于浏览,Builder 模式模仿了明确命名的可选参数。
留神 NutritionFacts 是不可变的,所有的默认参数都独自放在一个中央,Buildr 的设值办法返回 Builder 自身,以便把调用链接起来,失去一个 流式的 API。上面是调用示例:
???? 示例:
- 客户端调用无参的 Build 办法来生成是不可变的对象。
- 而后客户端在 Builder 对象上调用相似于 Setter 的办法来设置必要参数。
- 它不间接生成想要的对象,而是让客户端利用有必要的参数调用结构器,失去一个 Builder 对象。
它既能保障像重叠结构器模式那样的安全性,也能保障像 JavaBean 模式那么好的可读性
建造者模式
针对这种状况,书中给出了第三种代替形式。
类在结构过程中被分到了几个调用中,在结构过程中 JavaBean 可能处于不统一的状态
类无奈仅仅通过测验结构器参数的有效性来保障统一。
JavaBean 模式使得把类做成不可变的可能性不复存在(后边独自更新),须要付出额定的致力确保它的线程平安
美中不足的是,JavaBean 模式本身有一些毛病:
这种模式补救了重叠结构器的有余,创立实例很容易,这样的代码可读性也十分高:
这种模式下,先调用一个无参结构器来创建对象,而后通过调用 set 办法来设置每个必要的参数和可选参数,例如:
JavaBean 模式
这种状况下,还有第二种代替形式。
如果咱们想晓得具体的值是什么意思,必须认真地去读参数的正文。如果是一长串类型雷同的参数必然会导致一些奥妙的谬误,如果不小心颠倒了其中两个参数的程序,编译器并不会报错,然而最终的后果可能会呈现谬误的行为。
简而言之,重叠结构器模式可行,然而当有许多参数的时候,客户端的代码会很难编写,并且难以浏览。
其中的某个参数可能是咱们不想设置的,然而不得不为它们传递值。这个例子中,咱们给 fat 传递了一个值为 0。如果仅仅是 6 个参数,还在咱们的可控范畴,问题是随着性能和参数的减少,很快就会失去管制。
个别状况下,实例化对象的时候会变成以下这个样子: