依存関係の注入は、ctorで実行するか、メソッドごとに実行する必要がありますか?


16

考慮してください:

public class CtorInjectionExample
{
    public CtorInjectionExample(ISomeRepository SomeRepositoryIn, IOtherRepository OtherRepositoryIn)
    {
        this._someRepository = SomeRepositoryIn;
        this._otherRepository = OtherRepositoryIn;
    }

    public void SomeMethod()
    {
        //use this._someRepository
    }

    public void OtherMethod()
    {
        //use this._otherRepository
    }
}

に対して:

public class MethodInjectionExample
{
    public MethodInjectionExample()
    {
    }

    public void SomeMethod(ISomeRepository SomeRepositoryIn)
    {
        //use SomeRepositoryIn
    }

    public void OtherMethod(IOtherRepository OtherRepositoryIn)
    {
        //use OtherRepositoryIn
    }
}

Ctorインジェクションは拡張を困難にしますが(新しい依存関係が追加されたときにctorを呼び出すコードは更新する必要があります)、メソッドレベルのインジェクションはクラスレベルの依存関係からカプセル化されたままになり、これらのアプローチに対する/他の引数を見つけることができません。

注射のための決定的なアプローチはありますか?

(NB私はこれに関する情報を検索し、この質問を客観的にしようとしました。)


4
クラスが凝集している場合、多くの異なるメソッドが同じフィールドを使用します。これにより、求める答えが得られるはずです
...-Oded

@Oded Goodポイント、結束は決定に大きく影響します。たとえば、クラスがそれぞれ異なる依存関係(凝集性ではないが、論理的に類似している)のヘルパーメソッドのコレクションであり、別のクラスが凝集性が高い場合、それぞれ最も有益なアプローチを使用する必要があります。ただし、これによりソリューション全体で矛盾が発生します。
StuperUser

私が与えた例に関連:en.wikipedia.org/wiki/False_dilemma
StuperUser

回答:


3

依存するのは、注入されたオブジェクトがクラス内の複数のメソッドで使用されており、各メソッドに挿入しても意味がない場合、メソッド呼び出しごとに依存関係を解決する必要がありますが、依存関係が使用されるのは1つのメソッドをコンストラクターに挿入するのは適切ではありませんが、使用できないリソースを割り当てるためです。


15

まず最初に、「新しい依存関係が追加されたときに、ctorを呼び出すコードはすべて更新する必要があります」に対処しましょう。明確にするために、依存関係の注入を行っており、依存関係を持つオブジェクトでnew()を呼び出すコードがある場合、それは間違っています

DIコンテナは、関連するすべての依存関係を注入できる必要があります。そのため、コンストラクターの署名を変更する必要はありません。そのため、引数は実際には保持されません。

メソッドごとのインジェクションとクラスごとのインジェクションの考え方については、メソッドごとのインジェクションには2つの大きな問題があります。

1つの問題は、クラスのメソッドが依存関係を共有する必要があることです。多数の依存関係(おそらく4〜5以上)を持つクラスが表示された場合、そのクラスが最有力候補になります。 2つのクラスにリファクタリングします。

次の問題は、メソッドごとに依存関係を「注入」するために、それらをメソッド呼び出しに渡す必要があることです。これは、メソッド呼び出しの前に依存関係を解決する必要があることを意味します。したがって、おそらく次のようなコードの束になります。

var someDependency = ServiceLocator.Resolve<ISomeDependency>();
var something = classBeingInjected.DoStuff(someDependency);

ここで、アプリケーションの10か所でこのメソッドを呼び出すとしましょう。これらのスニペットは10個あります。次に、別の依存関係をDoStuff()に追加する必要があるとしましょう:そのスニペットを10回変更する必要があります(またはメソッドでラップする必要があります。この場合、DI動作を手動で複製するだけで無駄です)時間の)。

したがって、基本的にあなたがやったことは、DI利用クラスに独自のDIコンテナを認識させることです。これは根本的に悪い考えです

それをコンストラクター注入と比較してください。コンストラクターインジェクションでは、特定のDIコンテナーに縛られることはなく、クラスの依存関係を満たすことは直接責任を負わないため、メンテナンスはかなり頭痛のないものです。

無関係なヘルパーメソッドを含むクラスにIoCを適用しようとしているように思えますが、使用法に基づいてヘルパークラスをいくつかのサービスクラスに分割し、ヘルパーを使用して呼び出します。これはまだ素晴らしいアプローチではありません(単に渡される引数を処理するよりも複雑なことをするメソッドでクラス化されたヘルパーは、一般的に間違って記述されたサービスクラスにすぎません)が、少なくともデザインを少しきれいに保つでしょう。

(注:前に提案したアプローチを実行しましたが、それ以降は繰り返さないことは非常に悪い考えでした。結局、本当に分離する必要のないクラスを分離しようとしていました。各メソッド呼び出しが他のインターフェイスのほぼ固定された選択を必要とするインターフェイスのセットを使用します。これは維持するのに悪夢でした。)


1
"if you're doing dependency injection and you have any code calling new() on an object with dependencies, you're doing it wrong."クラスのメソッドを単体テストする場合は、インスタンス化する必要がありますか、DIを使用してテスト対象コードをインスタンス化しますか?
StuperUser

@StuperUserユニットテストは少し異なるシナリオです。私のコメントは実際に運用コードにのみ適用されます。それぞれが事前に構成された動作と一連の仮定とアサーションを備えたモックフレームワークを介してテストのモック(またはスタブ)依存関係を作成するので、それらを手動で注入する必要があります。
エドジェームズ

1
それは"any code calling the ctor will need updating"、私が言ったときに私が話しているコードです、私の生産コードはctorsを呼び出しません。これらの理由から。
StuperUser

2
@StuperUserかなり公平ですが、必要な依存関係でメソッドを呼び出すことになります。つまり、私の答えの残りはまだ残っています!正直に言うと、ユニットテストの観点から、コンストラクター注入とすべての依存関係の強制に関する問題に同意します。私はプロパティインジェクションを使用する傾向があります。これは、テストに必要な依存関係のみをインジェクトでき​​るため、実際にはコードを実際に記述することなく、暗黙的なAssert.WasNotCalled呼び出しの別のセットです!:D
エド・ジェームズ

3

control反転にはさまざまな種類があり、質問では2つしか提案されていません(ファクトリを使用して依存関係を注入することもできます)。

答えは次のとおりです。ニーズに依存します。ある種類と別の種類を使用した方が良い場合があります。

コンストラクターはオブジェクトを完全に初期化するためにあるので、個人的にはコンストラクターインジェクターが好きです。後でセッターを呼び出す必要があるため、オブジェクトは完全には構築されません。


3

少なくとも私にとって最も柔軟なもの、つまりプロパティインジェクションを逃しました。Castle / Windsorを使用し、クラスに新しいautoプロパティを追加するだけで、問題なく、インターフェイスを壊すことなく、注入されたオブジェクトを取得できます。


私はWebアプリに慣れていますが、これらのクラスがUIによって呼び出されるドメイン層にあり、プロパティインジェクションと同様の方法で依存関係が既に解決されている場合。私は、注入されたオブジェクトを渡すことができるクラスをどのように扱うのか疑問に思っています。
StuperUser

プロパティインジェクションも好きです。DIコンテナで通常のコンストラクタパラメータを使用して、解決された依存関係と未解決の依存関係を自動的に区別することができます。ただし、コンストラクターとプロパティの注入はこのシナリオでは機能的に同一なので、言及しないことにしました;)
エドジェームス

プロパティインジェクションは、すべてのオブジェクトを強制的に変更可能にし、無効な状態のインスタンスを作成します。なぜそれが望ましいのでしょうか?
クリスピットマン

2
@Chris実際には、通常の条件下では、コンストラクターとプロパティインジェクションはほぼ同じように動作し、オブジェクトは解決中に完全にインジェクトされます。「無効」と見なされるのは、DIコンテナを使用して実装をインスタンス化しない場合の単体テスト中のみです。これが実際に有益である理由についての私の見解については、私の答えに対するコメントを参照してください。可変性が問題になる可能性があることを感謝していますが、現実的には、インターフェースを介して解決されたクラスのみを参照し、編集する依存関係を公開しません。
エドジェームズ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.