3

C# プロジェクトで依存性注入を使用していますが、通常はすべて問題ありません。それにもかかわらず、「コンストラクターは単純な操作のみで構成する必要があります-依存関係を割り当てて、それ以上何もしない」というルールをよく耳にします。

//dependencies
interface IMyFooDependency
{
   string GetBuzz();
   int DoOtherStuff();
}

interface IMyBarDependency
{
  void CrunchMe();
}

//consumer
class MyNiceConsumer
{
  private readonly IMyFooDependency foo;
  private readonly  IMyBarDependency bar;
  private /*readonly*/ string buzz;//<---question here
  MyNiceConsumer(IMyFooDependency foo, IMyBarDependency bar)
  { 
     //omitting null checks
     this.foo = foo;
     this.bar = bar;
     //OR
     this.buzz = foo.GetBuzz();//is this a bad thing to do? 
  }  
}

UPDIMyFooDependency :を に置き換えることはできないと仮定しますGetBuzz()。その場合、答えは明らかです:「依存しないでくださいfoo」。

UPD2 : この質問は、仮想コードで foo から依存関係を排除することではなく、優れたコンストラクター設計の原則を理解することに関するものであることを理解してください。

したがって、私の質問は次のとおりです。これは、コンストラクターに重要なロジックを含めるのは本当に悪いパターンbuzzですか (つまり、値を取得し、依存関係に基づいていくつかの計算を行います)。

個人的には、遅延読み込みが必要でない限り、foo.GetBuzz()オブジェクトはコンストラクターへの呼び出し後に初期化する必要があるため、コンストラクターに含めます。

私が見る唯一の欠点: 重要なロジックを含めることで、何か問題が発生する可能性のある場所の数が増え、IoC コンテナーから難読化されたエラー メッセージが表示されます (ただし、無効なパラメーターの場合も同じことが起こるため、欠点はかなり小さいです)

重要なコンストラクターを省略するためのその他の考慮事項はありますか?

4

3 に答える 3

1

コンストラクターで作業を行わない理由は、プログラムの実行を 2 つのフェーズで見ることにあります。最初のフェーズは、オブジェクト グラフを接続することです。2 番目のフェーズは、「実際の作業」を行うことです。

この理想と、クラスの不変条件と内部状態を効率的に維持することとの間には、緊張関係があります。コンストラクターでできる設定が少ないほど、すべてのメソッドを実装するのが難しくなります。これは、オブジェクトのさまざまな内部状態を考慮に入れる必要があるためです。コンストラクターは、オブジェクトに対して確実に呼び出される唯一のコードであることを忘れないでください。

この難問から抜け出す方法は、オブジェクトの「実際の作業」が、他のオブジェクトとの関係におけるインターフェイスと動作によって定義されることを理解することです。つまり、コンストラクターに提供される依存関係と、後でメソッドへの引数として提供されるオブジェクトです。

システム内の他のオブジェクトに目立った影響を与えないコンストラクターで、好きな種類のセットアップを自由に行ってください。同様に、オブジェクトの構築におけるタイミングの問題にも非常に注意してください。

ユーザーが指定したファイル名がないと File オブジェクトが存在できないと判断した場合: コンストラクターで keyboard.filename_from_keyboard() を呼び出さないでください。代わりに、コンストラクターに提供されたファイル名を使用して実行中にファクトリ (プロバイダー) によってオブジェクトが作成されるようにシステムを設計するか、File オブジェクトがファイル名なしで存在できるようにします。実行中に独自のファイル名を取得できますか?これは、オブジェクトの有効期間を管理することの一部であり、IMO で最も難しい部分です。「実際の作業」にはオブジェクトの作成も含まれるため、これは非常に微妙になります。しかし、私は脱線します...

あなたの例では、 foo.GetBuzz() がその条件を破るかどうかを決定する必要があります。GetBuzz() が参照透過関数である場合、ほとんどの場合、コンストラクターでそれを呼び出すことは明確です。GetBuzz() に I/O やユーザーの操作が含まれる場合、または他のオブジェクトの顕著な内部状態が変更される場合は、おそらくコンストラクターから呼び出す必要はありません。

于 2012-05-15T16:36:04.950 に答える