web-dev-qa-db-ja.com

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

考慮してください:

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私はこれに関する情報を検索し、この質問を客観的にしようとしました。)

17
StuperUser

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

3
nohros

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

DIコンテナーは、関連するすべての依存関係を挿入できる必要があります。そのため、コンストラクターのシグネチャを変更することを心配する必要がなく、その引数は実際には保持されません。

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

1つの問題は、クラスのメソッドが依存関係を共有する必要があることです。これは、クラスが効果的に分離されていることを確認するための1つの方法です。多数の依存関係(おそらく4〜5を超える)があるクラスを見つけた場合、そのクラスが主要な候補です2つのクラスにリファクタリングするため。

次の問題は、メソッドごとに依存関係を「挿入」するために、それらをメソッド呼び出しに渡す必要があることです。つまり、メソッドを呼び出す前に依存関係を解決する必要があるため、次のような一連のコードが作成される可能性があります。

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

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

したがって、基本的にここで行ったことは、DIを利用するクラスが独自のDIコンテナーを認識できるようにすることです。これは基本的に悪いアイデアです。これは、維持するのが難しい不格好なデザインにすぐにつながるためです。 。

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

無関係な一連のヘルパーメソッドを含むクラスにIoCを適用しようとしているように思えますが、使用法に基づいてヘルパークラスをいくつかのサービスクラスに分割し、ヘルパーを使用して呼び出します。これはまだ優れたアプローチではありません(渡された引数を処理するだけでなく、より複雑な処理を行うメソッドでクラス化されたヘルパーは、一般に、不適切に記述されたサービスクラスです)。 。

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

15
Ed James

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

答えは、必要に応じて異なります。ある種類と別の種類を使用する方が良い場合もあります。

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

3
BЈовић

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

3
Otávio Décio