ビルダーパターンがこのように実装されることが多いのはなぜですか?


8

多くの場合、(Javaでの)ビルダーパターンの実装は次のようになります。

public class Foo {
    private Foo(FooBuilder builder) {
        // get alle the parameters from the builder and apply them to this instance
    }

    public static class FooBuilder {
        // ...
        public Foo build() {
            return new Foo(this); // <- this part is what irritates me
        }
    }
}

ここにも例:

なぜ人々はビルダーとタイプの間に強い(相互の)依存関係を導入して導入するのですか?

編集:つまり、その性質上、ビルダーはインスタンスを構築したいクラスに関連付けられていることを知っています(FooBuilder -> Foo)。私が質問しているのは:なぜ他の方法が必要なのですか(Foo -> FooBuilder

それどころかFoo、ビルダーが作成されたときに新しいインスタンスを作成Fooし、適切なビルダーメソッドが呼び出されたときにインスタンスにフィールドを設定する実装は(それほど頻繁ではありませんが)あります。流暢なAPIがまだあり、クラスをビルダーに関連付けないため、このアプローチの方が好きです。この他のアプローチの欠点はありますか?


あなたの質問は、Fooコンストラクタがパブリックの場合にも当てはまりますか?
2016

1
理由はDRYです。多くのフィールドがある場合、FooがFooBuilderを知らない場合、Fooのコンストラクターでそれらを繰り返す必要があります。このようにして、Fooのコンストラクターを保存します。
Esben Skov Pedersen

回答:


8

つまり、半分構築された状態でFooが存在しないことになります。

思い浮かぶいくつかの理由:

ビルダーは本来、構築しているクラスに関連付けられています。ビルダーの中間状態はコンストラクターの部分的な適用と考えることができるため、結合が強すぎるという主張は説得力がありません。

ビルダー全体を渡すことで、MyClassのフィールドを最終的にすることができ、MyClassについての推論が容易になります。フィールドがfinalでない場合は、ビルダーよりも流暢なセッターを簡単に使用できます。


1.その性質上、ビルダーはインスタンスを構築したいクラスに関連付けられています(FooBuilder-> Foo)。私が質問していること:なぜ他の方法が必要なのですか(Foo-> FooBuilder)2. 最終修飾子でポイントが表示されますが、その修飾子の他に、FooBuilderはFooのフィールドを設定できます(たとえFooBuilderはFooの一部であるため、プライベートです)。セッターを作成しない場合でも、インスタンスは変更できません。
アンディ

それはつまるところ決してそれだけで今までに内部ビルダーにある場合でも、半分構築状態ではFooを有していません。コンストラクターのオーバーロードセットが多いクラスで、コンストラクターとクラスの残りの部分の間に「強い(相互)依存関係」があったことが問題だと思いますか?それがビルダーとは何か-多くの同様のコンストラクターを簡潔に表現する方法
Caleth

答えをありがとう(一貫性のない状態にならないことについて)、それはそれを明確にします。答えは好きではありませんが、理解できました。それを組み込んでお答えいただければ、お受けいたします。
アンディ

3

ビルダーは、メソッドで設定された可変フィールドを保持します。実際のクラス(Foo)はfinal、ビルダーから不変フィールドを作成できます。

つまり、ビルダーはステートフルなクラスです。

しかし、ビルダーは他にも呼び出すことができたでしょうnew Foo(x, y, z)。それはより一方向の、より良いスタイルの私見でしょう。その時と同じように、アンチパターン、FooBuilderフィールドなどは不可能です。一方、FooBuilderはデータの完全性を保証します。

一方、FooBuilderは2つの役割を果たします。流れるようなAPIで構築するためと、コンストラクターにデータを提示するためです。コンストラクタのイベント型クラスです。過剰に設計されたコードでは、コンストラクターのパラメーターをいくつかのFooConstructorParamsに保持できます。


2

「ビルダー」は通常、特に不変クラスのコンテキストで「テレスコープコンストラクターの問題」を解決するために使用されるためです。本格的な例については、このリンクを参照してください。

代替案がどのようになるかを検討します。

 public static class FooBuilder {
    // works with immutable classes, but leads to telescoping constructor
    public Foo build() {
        return new Foo(par1, par2, par3, ....); 
    }
 }

または:

 public static class FooBuilder {
    // does not work for immutable classes:
    public Foo build() {
        var foo = new Foo(); 
        foo.Attribute1=par1;
        foo.Attribute2=par2;
        //...
        return foo;
    }
 }

したがって、特に不変クラスの場合、多数のパラメーターを持つコンストラクターが必要ない場合、実際には代替手段はありません。


私はビルダーパターンが解決する問題を知っています。しかし、実装がしばしば与えられた例のように見えるのはなぜかという私の質問には答えませんでした。あなたがリンクした記事のJosh Blochでさえそうではありません。しかし、カレスは彼/彼女のコメントで行います。
アンディ

@Andy:これがあなたの質問に答える理由を明確にするために例を挙げました。
Doc Brown

実際、2番目の例は必ずしも変更可能ではありません。セッターメソッドを実装しない場合は不変です。しかし、私は今、あなたが向かっているところを見ます。
アンディ

@Andy:C#のイディオムではなくJavaのイディオムを使用したい場合は、実際にセッターを使用して例を書き直すことができます。しかし、foo.Attribute1=par1可能であれば、FooJavaであっても間違いなく変更可能です。不変クラスは、セッターによっても、状態を変更するメソッドによっても、パブリックメンバー属性を公開することによっても、構築後の状態の変更を禁止する必要があります。
Doc Brown、

「特に不変クラスの場合」....しかし、セッターのないクラスは、外部からは不変に見えます。彼らが最終的であるかどうかは良いことです。
Esben Skov Pedersen
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.