オブジェクト初期化子でビルダーと流体インターフェースを使用することに意味がありますか?


10

JavaおよびC#では、パラメーターを使用してコンストラクターを定義するか、オブジェクトの作成後に各プロパティを定義するか、ビルダー/流体インターフェイスパターンを使用して、初期化時に設定できるプロパティを持つオブジェクトを作成できます。ただし、C#3ではオブジェクトとコレクションの初期化子が導入されたため、ビルダーパターンはほとんど役に立たなかった。イニシャライザのない言語では、ビルダーを実装して次のように使用できます。

Vehicle v = new Vehicle.Builder()
                    .manufacturer("Toyota")
                    .model("Camry")
                    .year(1997)
                    .colour(CarColours.Red)
                    .addSpecialFeature(new Feature.CDPlayer())
                    .addSpecialFeature(new Feature.SeatWarmer(4))
                    .build();

逆に、C#では次のように記述できます。

var vehicle = new Vehicle {
                Manufacturer = "Toyota",
                Model = "Camry",
                Year = 1997,
                Colour = CarColours.Red,
                SpecialFeatures = new List<SpecialFeature> {
                    new Feature.CDPlayer(),
                    new Feature.SeatWarmer { Seats = 4 }
                }
              }

...前の例のようにビルダーの必要性を排除します。

これらの例に基づいて、ビルダーはまだC#で​​有用ですか、それともイニシャライザによって完全に置き換えられましたか?


are builders usefulなぜ私はこの種の質問を見続けるのですか?Builderはデザインパターンです。言語機能として公開される可能性がありますが、結局のところ、それは単なるデザインパターンです。デザインパターン「間違っている」ことはできません。それはあなたのユースケースを満たすかもしれませんし、そうでないかもしれませんが、それはパターン全体を間違っているのではなく、それを適用するだけです。結局のところ、デザインパターンは特定の問題を解決することを忘れないでください。問題に直面しないのであれば、なぜそれを解決しようとするのでしょうか。
VLAZ、2016年

@vlaz私はビルダーが間違っていると言っているのではありません-実際、私の質問は、初期化子がビルダーを使用する最も一般的なケースの実装である場合、ビルダーのユースケースがあるかどうかを尋ねていました。明らかに、人々はビルダーがプライベートフィールドを設定するのに依然として有用であると答え、それが今度は私の質問に答えました。
svbnet 2016年

3
@vlaz明確にするために:初期化担当者は、内部のビルダークラスを作成する「従来の」ビルダー/流体インターフェイスパターンを大幅に置き換え、その親クラスの新しいインスタンスを作成するそのクラス内にチェーンセッターメソッドを作成すると言っています。イニシャライザが特定のビルダーパターンの代わりになると言っているのではありません。イニシャライザは、特定のビルダーパターンを実装する必要のある開発者を救い、回答に記載されているユースケースを除いて、ビルダーパターンを役に立たなくしないと言っています。
svbnet 2016年

5
@vlaz私はあなたの懸念を理解していません。「デザインパターンは役に立たないと言っていました」-いいえ、ジョーはデザインパターンが有用かどうか尋ねていました。悪い、間違った、または間違った質問はどうですか?答えは明白だと思いますか?答えは明白ではないと思います。これは私にとって良い質問のようです。
Tanner Swett、2016年

2
@vlaz、シングルトンとサービスロケータのパターンが間違っています。エルゴのデザインパターンは間違っている可能性があります。
David Arno

回答:


12

@ user248215が触れたように、本当の問題は不変性です。C#でビルダーを使用する理由は、設定可能なプロパティを公開する必要なく初期化子の明示性を維持するためです。カプセル化の問題ではないので、私は自分の答えを書きました。セッターの呼び出しは、セッターが実際に行うことや、その実装に結び付けることを意味しないため、カプセル化はかなり直交しています。

C#の次のバージョンである8.0は、with不変オブジェクトをビルダーを作成する必要なしに明確かつ簡潔に初期化できるようにするキーワードを導入する可能性があります。

イニシャライザとは対照的に、ビルダーで実行できるもう1つの興味深い点は、呼び出されるメソッドのシーケンスに応じて、さまざまなタイプのオブジェクトが生成される可能性があることです。

例えば

value.Match()
    .Case((DateTime d) => Console.WriteLine($"{d: yyyy-mm-dd}"))
    .Case((double d) => Console.WriteLine(Math.Round(d, 4));
    // void

var str = value.Match()
    .Case((DateTime d) => $"{d: yyyy-mm-dd}")
    .Case((double d) => Math.Round(d, 4).ToString())
    .ResultOrDefault(string.Empty);
    // string

上記の例を明確にするために、ケースを指定して「一致」を構築するためにビルダーパターンを使用するのは、パターンマッチングライブラリです。Case関数を渡すメソッドを呼び出すことにより、ケースが追加されます。valueが関数のパラメータタイプに割り当て可能な場合、それが呼び出されます。完全なソースコードはGitHubで見つけることができます。XMLコメントはプレーンテキストでは読みにくいため、ここにSandCastleビルドドキュメントリンクがあります(「備考」セクションを参照)。


IEnumerable<Func<T, TResult>>メンバーを使用してオブジェクトの初期化を行う方法がわかりません。
Caleth

@Calethそれは実際に私が実験したものですが、そのアプローチにはいくつかの問題があります。条件句(表示されないが、リンクされたドキュメントで使用および実証されている)は許可されておらず、の型推論も許可されていませんTResult。結局のところ、型推論はそれと関係が深い。また、制御構造のように見えるようにしたかったのです。また、ミューテーションが発生する可能性があるため、イニシャライザを使用したくありませんでした。
Aluan Haddad 2017

12

オブジェクト初期化子では、呼び出し元のコードからプロパティにアクセスできる必要があります。ネストされたビルダーは、クラスのプライベートメンバーにアクセスできます。

Vehicle(すべてのセッターをプライベートにすることで)不変にしたい場合は、ネストされたビルダーを使用してプライベート変数を設定できます。


0

それらはすべて異なる目的を果たします!!!

コンストラクターは、マークさreadonlyれたフィールドと、プライベートおよび保護されたメンバーを初期化できます。ただし、コンストラクター内で実行できることには多少制限があります。thisたとえば、外部メンバーに渡したり、仮想メンバーを呼び出したりすることは避けてください。仮想メンバーは、まだ構築されていない派生クラスのコンテキストで実行される可能性があるためです。また、コンストラクターは必ず実行されることが保証されているため(呼び出し元が非常に異常なことをしている場合を除く)、コンストラクターでフィールドを設定すると、クラスの残りの部分のコードは、これらのフィールドがnullにならないと想定できます。

初期化子は構築後に実行されます。パブリックプロパティのみを呼び出すことができます。プライベートフィールドや読み取り専用フィールドは設定できません。慣例により、プロパティを設定する動作はかなり制限されている必要があります。たとえば、べき等であり、副作用が制限されている必要があります。

ビルダーメソッドは実際のメソッドであるため、複数の引数を使用でき、慣例により、オブジェクトの作成などの副作用が生じる可能性があります。読み取り専用フィールドを設定することはできませんが、他のほとんどのことを行うことができます。また、メソッドは拡張メソッドとして実装できます(ほとんどすべてのLINQ機能と同様)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.