Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/351.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Builder设计模式类中的冗余字段声明及其生成器_Java_Builder Pattern - Fatal编程技术网

Java Builder设计模式类中的冗余字段声明及其生成器

Java Builder设计模式类中的冗余字段声明及其生成器,java,builder-pattern,Java,Builder Pattern,经典构建器模式要求在要构建的类中声明字段,并在构建器类中声明完全相同的字段。如果有许多字段,并且在重构过程中字段类型不同步,这可能会导致问题。以下是我的意思示例(我从Joshua Block的一篇文章中借用了这个代码示例): 现在,让我们假设servingSize需要从int更改为long,并且此更改在NutritonFacts中完成,但由于意外,在静态Builder中也没有完成 诚然,问题较少的是字段的数量NutritionFacts有6个字段,因此,Builder也有6个字段。如果有20或1

经典构建器模式要求在要构建的类中声明字段,并在构建器类中声明完全相同的字段。如果有许多字段,并且在重构过程中字段类型不同步,这可能会导致问题。以下是我的意思示例(我从Joshua Block的一篇文章中借用了这个代码示例):

现在,让我们假设
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;
    }
}