2017-08-06 60 views
1

,我已經遇到網上通常有這樣的事情嵌套生成器模式: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); 
     } 

    } 

} 

我的問題是,是否有必要在複製和Person領域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; 
     } 

    } 

} 

注:我理解這個話題可能剛愎自用,有可能不是一個「正確」的答案,但我只是想聽到不同的想法和意見。我不是在尋找ultimate truth

+2

在第二個例子中,您完全擊敗了構建器的要點。構建器構建_complete_,_valid_和_immutable_實例 - 現在您無法完成這些任務。更糟糕的是,構建器可以在調用'build'後修改構建的對象**。我沒有看到「重複的字段」有任何問題 - 但是您提出的替代方案非常糟糕。 –

+0

我明白你的意思了,謝謝你的反饋 – user2914191

+0

什麼樣的第一個評論說是不對的恕我直言。在構建方法被調用之前,構建器可以做任何它需要做的事情。而且你的代碼沒有被證明是錯誤的或正確的(這取決於你沒有顯示的代碼)。建設者可以在建立過程中隨意修改對象。唯一的必須是構建器之外的任何人都不能修改它,並且一旦構建方法被稱爲noone(甚至不是構建器)都可以修改它。你展示的人的成員是私人的,不變的,所以一切都很好。這裏的關鍵是如果你有一個公開的二傳手。 –

回答

2

您的代碼就可以了,只要:

  1. 你把你的個人成員變量私人(你這樣做)
  2. 你不提供,讓這些成員變量的修飾方法(在你顯示的代碼不會這樣做,但是你省略了部分代碼)
  3. 這些成員變量是不可變的,或者你確保getter提供它們的副本。通常更好的是成員已經是不可變的了(提示:甚至java集合)。否則您將在每個getX調用中創建實例。
  4. 一旦調用了Builder.build,沒有人必須能夠修改Person實例狀態,甚至不需要Builder本身。 這不會發生在您發佈的代碼中
  5. 構建器不公開正在構建的「時態實例」(如果有的話)。沒有一個實例必須暴露在構建方法的返回之外。

有關於哪種方式是首選方式或大多數時候不喜歡味道的意見。但就正確與否而言,這種方法在經過一些修改後會很好。最後,調用構建之前發生的事情純粹是Builder的內部。這是一個實施問題。重要的是以前的規則得到滿足。

要修復規則4:您的Builder.build方法應返回正在使用的臨時實例的深度克隆(有多種方法可以無需指定每個字段)。或者,您應該在構建器中有一個標誌,禁止在構建實例中調用任何其他方法,一旦構建被調用。

附註:我通常更喜歡Builder類也使用私有構造函數。我想有這樣的Person類:

public static Builder builder() { 
    return new Builder(); 
} 

這可以給你初始化生成器的方式更靈活,甚至可以有幾種生成器方法做的不完全一樣的東西,在「預」的條款(並且因爲它們是方法,所以在命名上比在構造函數上更靈活:))

+0

如上所述;代碼不好。事實上,它具體違反了你的觀點4. –

+0

遵循你的定義 - 你是如何發現第二種方法完美無缺的?在調用構建之後,可以調用id方法並修改返回的對象的ID。 這裏的另一個奇怪的事情,雖然不是非常重要,但是你在調用構建方法之前創建Person對象。 – user265732

+1

你是對的,我沒有注意到build是返回person instance的。它應該在構建器中返回一個克隆/副本或者至少有一個標誌來禁止在調用構建之後調用任何其他構建器實例方法。將更新回答 –