在正确的位置使用正确的构建器

时间:2013-02-10 23:24:11

标签: java multithreading design-patterns builder qa

我对建造者模式非常感兴趣,我经常使用它,但我不确定我制造的建造者是否足够好,而且我对所有可以使用它们的周围环境都有疑问。 这是我如何创建构建器的示例:

public class Person {

    private String name;
    private String secondName;
    private int age;

    public static class Builder {
        private boolean isBuilt;
        private Person person = new Person();

        private void check() {
            if (isBuilt) {
                throw new IllegalStateException(
                        "The object cannot be modified after built");
            }
        }

        public Builder withName(String name) {
            check();
            person.name = name;
            return this;
        }

        public Builder withSecondName(String secondName) {
            check();
            person.secondName = secondName;
            return this;
        }

        public Builder withAge(int age) {
            check();
            person.age = age;
            return this;
        }

        public Person build() {
            check();
            isBuilt = true;
            return person;
        }
    }

    @Override
    public String toString() {
        return "Name: " + name + "\nSecond name:" + secondName + "\nAge:" + age;
    }
}

快速使用示例:

Person person = new Person.Builder()
        .withName("John")
        .withSecondName("Smith")
        .withAge(50)
        .build();
        System.out.println(person);

这里有些疑点:

  • 你认为它真的是不变的吗?如果不是,我该如何改进呢?
  • 关于线程安全。那么这可能是我的主要疑问。这真的是线程安全吗? 我在互联网上看到一些例子,说类级变量必须是最终的,并通过构造函数传递。我还看到了一个变量被声明为volatile的例子。你觉得怎么样?
  • 您是否认为此构建器在可以使用的场景方面有任何限制?我的意思是它适合在EJB,JSF支持bean,MDB中调用,还是成为JPA实体......?

1 个答案:

答案 0 :(得分:1)

  

你认为它真的是不变的吗? [...]这真的是线程安全吗?

您的代码中没有任何部分是不可变的。这也可能妨碍线程安全;这表示很难以二进制方式声明一个类是否是线程安全的。我也不明白为什么你会首先在线程之间共享构建器实例,但是我可能会因代码示例的简单性而误导。

为了更轻松地实现线程安全,您的Builder应该是不可变的。这意味着每个withXXX()方法都应返回一个表示新状态的新构建器。 (这可能是更聪明的方法,但这将是直截了当的方法。)

重申一下:我不确定是否必须使构建器线程安全 - 大多数情况下它们是具有非常短的生命周期和可见范围的对象。是否要使它们不可变取决于用例,您可能希望存储部分填充的构建器,但这也很少见。 (主观上,对于名称以with开头的方法而言,它似乎更直观,不能就地修改对象,而不是名称以set开头的对象。)

  

您是否认为此构建器在可以使用的场景方面有任何限制?

这通常是无法回答的,但是如果你确实使你的Person对象不可变,因此只能由你的构建器构建,它们将无法用作JPA实体,我的猜测也是JSF支持bean。为您创建/管理某些对象的Java框架通常比不期望它们是JavaBeans,这意味着可以通过反射调用no-args构造函数和属性setter来创建这些对象。