2017-08-06 2 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); 
     } 

    } 

} 

私の質問ですが、それはPersonBuilderのフィールドを複製する必要があるのでしょうか?それは冗長なコードのように思える。そして私の2番目の質問は、次のコードが実行可能な代替品になるのか、なぜ、なぜそうではないのでしょうか?

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

2番目のインスタンスでは、ビルダーのポイントを完全に破棄しました。ビルダーが_complete_、_valid_および_immutable_インスタンスをビルドします。あなたのインスタンスはこれらのものを一切持たないようになりました。さらに悪いことは、**ビルドが呼び出された後にビルダーがビルドされたオブジェクトを変更できることです。私は "重複フィールド"には何の問題も見ません - しかしあなたの提案された選択肢はひどいです。 –

+0

フィードバックのおかげで何を意味するのか見る – user2914191

+0

最初のコメントは正しいIMHOではないと言いました。ビルダーは、ビルドメソッドが呼び出される前に、必要な処理を行うことができます。そしてあなたのコードは間違っているか正しくないことを証明していません(あなたが表示していないコードに依存します)。ビルダーは、ビルド中にオブジェクトを変更することができます。唯一のことは、ビルダーの外に誰もそれを変更できなければならないことです。そして、ビルドメソッドが一度もnoone(ビルダーさえも)と呼ばれると、それを変更することができます。 Personのメンバーはプライベートで不変なので、すべてうまくいきます。ここで重要なのは、あなたがパブリックセッターを持っているかどうかです。 –

答えて

2

あなたのコードは限り大丈夫だと思う:あなたは(あなたがそうしている)プライベートあなたの人のメンバ変数を保つ

  1. あなたはそれらのメンバ変数の変更を許可する方法(提供されていません。表示されているコードは実行されませんが、その一部は省略されています)
  2. これらのメンバー変数は不変です。通常、メンバはすでに不変です(ヒント:Javaコレクション)。それ以外の場合は、各getX呼び出しでインスタンスを作成します。
  3. Builder.buildが呼び出されると、Builder自体でなく、Personインスタンスの状態を変更することができなくてはなりません。 投稿したコードではこれが起こっていません
  4. ビルダーは「一時的なインスタンス」が構築されていることを明らかにしていません。インスタンスは、ビルドメソッドの返却を除いて公開される必要はありません。

好みの方法であるかどうかについての意見があり、ほとんどの場合味の問題です。しかし、正しいかどうかという点では、そのアプローチはいくつかの変更を加えても問題ありません。最後に、ビルドが呼び出される前に何が起こったかは、ビルダーの純粋なものです。実装の問題です。重要なことは、以前のルールが満たされていることです。

ルール4を修正するには、Builder.buildメソッドは、使用されているtempインスタンスの深いクローンを返す必要があります(各フィールドを指定する必要なしに、それを回避する方法があります)。あるいは、ビルダーが呼び出された後、ビルダーインスタンスの他のメソッドを呼び出すことを禁じるビルダーのフラグが必要です。

サイドノート:私は通常、Builderクラスもプライベートコンストラクタを使用することをお勧めします。私は、Personクラスでこれを持っています:

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

は、これはあなたのビルダーを初期化する方法についてより多くの柔軟性を与えることができ、あるいは、あなたは「事前設定」の面でまったく同じものではなくやっていくつかのビルダー・メソッドを持つことができますビルダー(そしてそれらはメソッドなので、コンストラクターよりも命名に柔軟性があります:))

+0

上記のように、コードがうまくないです。実際、具体的には、それはあなたのポイント4に違反しています。 –

+0

あなたの定義に従えば、どのように2番目のアプローチが完璧にうまくいくのでしょうか? buildを呼び出した後、idメソッドを呼び出して、返されたオブジェクトのIDを変更することができます。ここ 別の奇妙なことは、非常に重要ではないが、ビルドメソッドを呼び出す前に、Personオブジェクトを作成することです。 – user265732

+1

あなたは正しいです、私はビルドが人のインスタンスを返すように気づいていませんでした。これは、クローン/コピーを返すか、ビルドが呼び出された後に他のビルダーインスタンスメソッドを呼び出すことを禁止するために少なくともビルダーにフラグを持っていなければなりません。ビルドメソッドの前に作成されたインスタンスについてのあなたの懸念について、答え –

関連する問題