java嵌套构建器模式重复字段

use*_*191 6 java

我在网上遇到的嵌套构建器模式通常是这样的:

class Person{

    private int id;
    private String name;
    private int age;
    ... so on

    private Person(Builder builder){
        this.id = builder.id;
        this.name = builder.name;
        this.age = builder.age;
    }

    public static class Builder{

        private int id;
        private String name;
        private int age;
        ... so on

        public Builder id(int id){
            this.id = id;
            return this;
        }
        public Builder name(String name){
             this.name = name;
             return this;
        }
        .... so on

        public Person build(){
            return new Person(this);
        }

    }

}
Run Code Online (Sandbox Code Playgroud)

我的问题是,是否有必要在Personand 中复制字段Builder?好像有很多多余的代码。我的第二个问题是,以下代码是否可以替代,为什么或为什么不可行?

class Person{

    private int id;
    private String name;
    private int age;
    ... so on

    private Person(){}

    public static class Builder{

        private Person person = new Person();

        public Builder id(int id){
            this.person.id = id;
            return this;
        }
        public Builder name(String name){
             this.person.name = name;
             return this;
        }
        .... so on

        public Person build(){
            return person;
        }
        // UPDATED -- another build method
        public Person build(){
            Person built = this.person;
            this.person = new Person();
            return built;
        }

    }

}
Run Code Online (Sandbox Code Playgroud)

注意:我知道这个话题可能是自以为是,可能没有“正确”的答案,但我只是想听听不同的想法和意见。我不是在寻找ultimate truth.

alb*_*nil 3

只要满足以下条件,您的代码就可以了:

  1. 您将 Person 成员变量保留为私有(您正在这样做)
  2. 您不提供允许修改这些成员变量的方法(您显示的代码没有这样做,但您省略了其中的部分内容)
  3. 这些成员变量是不可变的,或者您确保 getter 提供它们的副本。通常最好成员已经是不可变的(提示:甚至是 java 集合)。否则您将在每个 getX 调用上创建实例。
  4. 一旦 Builder.build 被调用,任何人都不能修改 Person 实例状态,甚至 Builder 本身也不能。您发布的代码中没有发生这种情况
  5. 构建器不会公开正在构建的“临时实例”(如果有的话)。除了构建方法的返回之外,不得公开任何实例。

对于哪种方式是首选或不是首选方式存在不同意见,大多数时候都是品味问题。但就正确与否而言,这种方法经过一些修改就可以了。最后,调用构建之前发生的事情纯粹是构建器内部的。这是一个执行问题。重要的是满足之前的规则。

要修复规则 4:您的 Builder.build 方法应返回正在使用的临时实例的深度克隆(有多种方法可以实现这一点,而无需指定每个字段)。或者,您应该在构建器中设置一个标志,一旦调用构建后,禁止在构建器实例上调用任何其他方法。

旁注:我通常更喜欢 Builder 类也使用私有构造函数。我会把这个放在 Person 类上:

public static Builder builder() {
    return new Builder();
}
Run Code Online (Sandbox Code Playgroud)

这可以为您在初始化构建器的方式上提供更大的灵活性,甚至您可以让多个构建器方法在“预配置”构建器方面执行不完全相同的操作(并且由于它们是方法,因此在命名方面比关于构造函数:))