Java Builder设计模式类中的冗余字段声明及其生成器
经典构建器模式要求在要构建的类中声明字段,并在构建器类中声明完全相同的字段。如果有许多字段,并且在重构过程中字段类型不同步,这可能会导致问题。以下是我的意思示例(我从Joshua Block的一篇文章中借用了这个代码示例): 现在,让我们假设Java Builder设计模式类中的冗余字段声明及其生成器,java,builder-pattern,Java,Builder Pattern,经典构建器模式要求在要构建的类中声明字段,并在构建器类中声明完全相同的字段。如果有许多字段,并且在重构过程中字段类型不同步,这可能会导致问题。以下是我的意思示例(我从Joshua Block的一篇文章中借用了这个代码示例): 现在,让我们假设servingSize需要从int更改为long,并且此更改在NutritonFacts中完成,但由于意外,在静态Builder中也没有完成 诚然,问题较少的是字段的数量NutritionFacts有6个字段,因此,Builder也有6个字段。如果有20或1
servingSize
需要从int
更改为long
,并且此更改在NutritonFacts
中完成,但由于意外,在静态Builder
中也没有完成
诚然,问题较少的是字段的数量
NutritionFacts
有6个字段,因此,Builder
也有6个字段。如果有20或100个字段呢?在NutritionFacts
和Builder
中复制它们将是真正的痛苦。是否有更好的方法可以避免所有重复和潜在的类型同步错误?您可以使用NutritionFacts
对象来存储生成器的状态:
// 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;
}
}
您可以使用步骤生成器模式增强经典生成器模式,以构建无脑界面、易于使用、不可能出错的对象。有关更多详细信息,请参见此。我知道您最初发布(但被编辑掉)的解决方案存在问题,因为为
NutritionFacts
引入无参数构造函数导致编译器错误,因为所有字段都声明为final
。但是为什么它们需要被声明为final呢NutritionFacts
没有设置程序,第二次调用Builder
的build()
方法只会创建NutritionFacts
的另一个实例,而不是修改原始的字段。我认为您最初的解决方案,但没有最终的
修饰符,才是正确的选择。是吗?是的。。。只需改变新的营养因子(0,0,0,0,0)代码>至新营养学事实()代码>我已编辑我的答案,以包含两个选项的代码。我还更喜欢“定义不变”的方法,因为它使setter更加紧凑。如果您对final
状态没有要求,那么是的-这就是方法。NutritionFacts
对象的消费者(最明确的意思是双关语!)一旦实例化就不能修改对象。诚然,程序员可以修改类来修改字段,因为它不再声明为最终状态,但这是一个可以接受的权衡。谢谢你。它非常简单和干净。
// 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;
}
}