经典的Builder Pattern要求在要构建的类中声明字段,并在构建器类中声明完全相同的字段.当存在许多字段时,这会导致问题,并且在重构期间,字段类型不会保持同步.这是我的意思的一个例子(我从Joshua Block的一篇文章中借用了这个代码示例):
// Builder Pattern
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 {
private int servingSize = 0;
private int servings = 0;
private int calories = 0;
private int fat = 0;
private int carbohydrate = 0;
private int sodium = 0;
public Builder servingSize(int val)
{ servingSize = val; return this; }
public Builder servings(int val)
{ servings = val; return this; }
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;
}
}
现在让我们假设serveSize需要从int更改为long,并且这个更改是在NutritonFacts中完成的,但是,不可能在静态Builder中完成.
无可比拟的问题是字段的数量. NutritionFacts有6个领域,因此,Builder也是如此.如果有20或100个字段怎么办?在NutritionFacts和Builder中复制它们将是真正的痛苦.有没有更好的方法可以避免所有重复和类型同步错误的可能性?
解决方法:
您可以使用NutritionFacts对象来存储Builder的状态:
// Builder Pattern
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 {
private NutritionFacts state = new NutritionFacts(0,0,0,0,0,0);
public Builder servingSize(int val) {
state = new NutritionFacts(val, state.servings, state.calories, state.fat, state.sodium, state.carbohydrate);
return this;
}
[...]
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
servingSize = builder.state.servingSize;
servings = builder.state.servings;
calories = builder.state.calories;
fat = builder.state.fat;
sodium = builder.state.sodium;
carbohydrate = builder.state.carbohydrate;
}
private 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是不可变的,因此需要为每次更改构建一个新的状态对象,这可能是也可能不值得.
如果你可以使NutritionFacts的内部状态相互作用会更容易,但是使用私有的setter – 根据定义使对象不可变,而不是使用final关键字:
// Builder Pattern
public class NutritionFacts {
private int servingSize = 0;
private int servings = 0;
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public static class Builder {
private NutritionFacts state = new NutritionFacts();
public Builder servingSize(int val) {
state.servingSize = val;
return this;
}
public Builder servings(int val) {
state.servings = val;
return this;
}
[...]
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
servingSize = builder.state.servingSize;
servings = builder.state.servings;
calories = builder.state.calories;
fat = builder.state.fat;
sodium = builder.state.sodium;
carbohydrate = builder.state.carbohydrate;
}
}
标签:builder-pattern,java 来源: https://codeday.me/bug/20190830/1765758.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。