2013-02-10 152 views
3

我对建筑师模式非常感兴趣,我经常使用它,但我不确定我制造的建造者是否足够好,并且我也怀疑我可以使用它们的所有环境。 这是我如何创建一个建设者的例子:在正确的地方使用正确的建造者

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

'Builder'应该和'Person'有相同的字段 - 不是另一个'Person'在里面,'Person'中的所有字段都应该是'final'。 – 2013-02-10 23:33:06

+0

我不是这是一个综合性问题的粉丝。你还应该考虑删除诸如“你喜欢我的代码如何”或“我如何命名”等主观问题。 – millimoose 2013-02-10 23:39:20

+0

@LouisWasserman看看这个链接,我发现有人也是这样做的。我想删除重复,但我不知道如果我做得很好:http://xavimiro.blogspot.ie/2008/04/new-builder-pattern.html他建议'volatile'关键字,但我是不确定在线程安全方面这是否完全有效。 – sfrj 2013-02-10 23:46:05

回答

1

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

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

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

只是为了重申,虽然:我不知道它是绝对必要使建设者线程安全 - 大多数时间他们是具有非常短的生命周期和可视范围的对象。无论你想让它们不可变都取决于用例,你可能想要存储部分填充的构建器,但这也很少见。 (主观上它确实然而似乎对他的名字与with开始不修改就地对象的方法更为直观,而不是一个名字以set开始。)

你认为这建设者将有关于可以使用的场景的任何限制?

这通常是无法回答的,但如果你让你的Person对象不变,并通过您的建设者因此只构造的,他们将不能用作JPA实体,我的猜测是JSF支持豆类为好。为您创建/管理某些对象的Java框架通常比不指望它们是JavaBeans,这意味着可以通过反射调用无参数构造函数和属性设置器来创建这些对象。

+0

感谢您的回答。在提及JPA和JSF时非常有趣。我想JPA提供的替代方法是'CDI',它允许注入Pojos。你对此有何看法? 你说有可能更聪明的方式来实现它,如果你用一个小例子更新答案,我将不胜感激。 – sfrj 2013-02-10 23:56:27

+0

@sfrj更聪明的方法包括做一些事情,比如返回一个更合作的'XxxBuilder',用它们的类型来表明哪些操作是有效的。本质上,你使用构建器来建模一个代表对象构造的状态机,其中'withXxx()'方法是状态转换,'build()'方法是终端状态。我不知道在你的例子中构建数据结构是否可行。 (或者构建器模式是否适用于开始的数据结构。) – millimoose 2013-02-11 00:01:51

+0

@sfrj“我想JPA提供的替代方案是允许注入POJO的CDI,您怎么看待这个问题?” - 我想你会试图让我向你解释Java EE技术的随机流行语。但不管怎么说。我没有看到JPA以何种方式提供CDI,我非常怀疑您可以让CDI创建JPA实体,所以我不确定为什么要将这两者连接起来。 CDI支持构造器注入,所以它*可以*让你管理不可变的支持bean,但它们仍然不会被你的构建器构建,所以这点没有什么意义。 – millimoose 2013-02-11 00:04:58