2014-01-10 26 views
1

我正在嘗試爲我的班級使用生成器模式..如何在構建器模式中使用默認值(如果該值未通過並且使線程安全?

下面是我的Builder類,我按照Joshua Bloch版本構建,正如他在Effective Java,2nd Edition中所展示的。我們的客戶將主要通過userId,clientId總是,但其他領域是可選的,他們可能會或可能不會通過它。這裏Preference是一個ENUM類,它有四個字段。

public final class InputKeys { 

    private long userid; 
    private long clientid; 
    private long timeout = 500L; 
    private Preference pref; 
    private boolean debug; 
    private Map<String, String> parameterMap; 

    private InputKeys(Builder builder) { 
     this.userid = builder.userId; 
     this.clientid = builder.clientId; 
     this.pref = builder.preference; 
     this.parameterMap = builder.parameterMap; 
     this.timeout = builder.timeout; 
     this.debug = builder.debug; 
    } 

    public static class Builder { 
     protected final long userId; 
     protected final long clientId; 
     protected long timeout; 
     protected Preference preference; 
     protected boolean debug; 
     protected Map<String, String> parameterMap; 

     public Builder(long userId, long clientId) { 
      this.userId = userId; 
      this.clientId = clientId; 
     } 

     public Builder parameterMap(Map<String, String> parameterMap) { 
      this.parameterMap = parameterMap; 
      return this; 
     } 

     public Builder preference(Preference preference) { 
      this.preference = preference; 
      return this; 
     } 

     public Builder debug(boolean debug) { 
      this.debug = debug; 
      return this; 
     } 

     public Builder timeout(long timeout) { 
      this.timeout = timeout; 
      return this; 
     } 

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

下面是我的枚舉類 -

public enum Preference { 
    PRIMARY, 
    SECONDARY 
}  

我提出這樣的呼籲構建InputKeys參數 -

InputKeys keys = new InputKeys.Builder(12000L, 33L).build();  
System.out.println(keys); 

唯一的問題在這裏,我是看到,如果客戶沒有通過任何超時值,我需要將默認超時值始終設置爲500,但如果它們傳遞任何超時值,則應該覆蓋默認超時值。而這對我來說並不適用,因爲當我在調試模式下看到我的keys時,它總是將超時值設爲0.

有什麼我不見了?而且我正在試圖讓這個類不可變和線程安全..這是一個線程安全和不可變的生成器或我已經錯過了一些東西?

更新: - 當人們將使用該

具體情形是,我們有一個工廠,客戶會使用,使我們的代碼的調用。我們有一個簡單的接口,我們的一個類正在實現它,然後我們有一個工廠,我們將通過該工廠調用該實現的某個方法,該方法接受此參數keys

因此,他們將使用下面的代碼在那裏的應用程序進行調用,它可能會在多線程環境中運行在那裏的應用程序。

InputKeys keys = new InputKeys.Builder(12000L, 33L).build();  

IClient client = ClientFactory.getInstance(); 
client.getData(keys); 

然後InputKeys Builder可以多次使用來構建的鑰匙,這不是一次。不管userId和clientId他們會得到他們將再次構建InputKeys。

回答

2

只需將Builder中的超時初始化爲500即可。如果它被調用,timeout方法將覆蓋此值。

protected long timeout = 500L; 
+0

謝謝。我應該從'InputKeys'類中移除它。正確?那麼不變性和線程安全呢?它看起來不錯嗎? – AKIWEB

+0

我現在沒有理由在'InputKeys'類中擁有'500'。爲了不變性,我會使'InputKeys'變量'final'。這不是必要的,但它清楚地表明價值觀不應改變。如果您決定在'InputKeys'中實現getter,則返回'parameterMap'或'Collections.unmodifiableMap(parameterMap)'的副本,以防止修改地圖對象。至於線程安全性,我不知道在多線程中共享「Builder」的情況。 – rgettman

+0

謝謝。不知何故,當我試圖實例化InputKeys類時,Collections.unmodifiableMap(parameterMap)給了我NPE,所以我刪除了它。此外,我也更新了我的問題。 – AKIWEB

3

可以初始化在Builder構造的超時值(或變量聲明),例如

public Builder(long userId, long clientId) { 
    this.userId = userId; 
    this.clientId = clientId; 
    this.timeout = 500L; 
} 

這樣,如果你不叫timeout()方法,它將具有默認值,否則將被設置爲通過timeout()傳遞的值。

關於線程安全,如果Builder構造僅用於創建新InputKeys對象(如在在問題中描述的情形),而不必訪問該InputKeys對象的代碼的任何其它區域,也沒有需要應用任何額外的保護。

+0

感謝您的建議。我只是用更多的細節更新了這個問題。 – AKIWEB

+0

Builder構造函數也可以使用多次。這不僅是一個電話。 'userId'在每次調用時都會一直改變,如果他們決定使用其他參數,那麼它也會隨着每次調用一起進行。 – AKIWEB

+0

多次使用Builder構造函數是很好的,只要你不需要在不同的線程中共享由它創建的InputKeys對象。每次使用構造函數時,都會生成一個新的InputKeys對象。 – PNS

相關問題