2013-02-10 159 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