web-dev-qa-db-ja.com

依存性注入のスタイルの実際的な違いは何ですか?

ディペンデンシーインジェクション は初めてですが、アプリケーションで使用するスタイルについていくつか質問があります。 Martin FowlerによるInversion of Control Containers and the Dependency Injection patternを読んだばかりですが、実用的ではありませんコンストラクタ、セッター、インターフェース注入の違い。

どちらか一方を使用する理由は、コードのクリーンアップや明確化の問題にすぎないと思われます。違いはなんですか?どちらか一方を使用することに強い利点や欠点はありますか、それとも私が前に述べたとおりですか?

私の意見では、 コンストラクタインジェクション はすべての中で最も直感的であり、インターフェースの注入が最小です。一方、セッターインジェクションは中期的な用語ですが、最初にインジェクトした依存関係オブジェクトのインスタンスを変更できるはずですか?このスタイルの注入は、依存関係を必要とするオブジェクトが常に注入されることを保証しますか?私は信じていませんが、私が間違っている場合は修正してください。

12
ecampver

Constructor Injectionには、依存関係を明示的にし、クライアントにインスタンスを提供させるという利点があります。また、クライアントが後でインスタンスを変更できないことも保証できます。 1つの(可能性のある)欠点は、コンストラクターにパラメーターを追加する必要があることです。

Setter Injectionには、コンストラクタにパラメータを追加する必要がないという利点があります。また、クライアントがインスタンスを設定する必要もありません。これは、オプションの依存関係に役立ちます。これは、たとえば、デフォルトで実際のデータリポジトリをクラスに作成させ、テストでセッターを使用してそれをテストインスタンスに置き換える場合にも役立ちます。

Interface Injectionは、私の知る限り、setter注入と大差ありません。どちらの場合も、(オプションで)後で変更できる依存関係を設定します。

最終的には、preferenceと依存関係がrequiredであるかどうかの問題です。個人的には、コンストラクタインジェクションをほぼ排他的に使用しています。クライアントにコンストラクターでインスタンスを提供させることで、クラスの依存関係を明示的にするのが好きです。また、事後にクライアントがインスタンスを変更できないことも気に入っています。

多くの場合、2つの異なる実装を渡す唯一の理由は、テストのためです。本番環境ではDataRepositoryを渡しますが、テストではFakeDataRepositoryを渡します。この場合、通常は2つのコンストラクターを提供します。1つはパラメーターなし、もう1つはIDataRepositoryを受け入れます。次に、パラメーターのないコンストラクターで、2番目のコンストラクターへの呼び出しをチェーンし、new DataRepository()を渡します。

C#の例を次に示します。

_
public class Foo
{
  private readonly IDataRepository dataRepository;

  public Foo() : this(new DataRepository())
  {
  }

  public Foo(IDataRespository dataRepository)
  {
    this.dataRepository = dataRepository;
  }
}
_

これは、貧乏人の依存性注入として知られています。プロダクションクライアントコードでは、次のようなステートメントを繰り返して繰り返す必要がないため、気に入っています。

var foo = new Foo(new DataRepository());
12
jhewlett

コンストラクターとセッターインジェクションの違いについては既に上記で十分に説明しているため、ここでは詳しく説明しません。

インターフェース注入は、それを使用するオブジェクトの初期化中ではなく、使用時に依存関係を決定できるため、便利な注入のより高度な形式です。これにより、いくつかの便利なオプションが可能になります。

  • 依存関係のスコープは、注入先のオブジェクトとは異なります。たとえば、インターフェイスインジェクションを使用して、ユーザーセッションごとまたはスレッドごとに存在するオブジェクトをグローバルシングルトンに提供できます。オブジェクトが依存関係を必要とするたびに、フレームワークが提供するゲッターメソッドを呼び出します。これにより、オブジェクトが呼び出される状況に応じて異なる結果が返される可能性があります。

  • 遅延初期化が可能です-使用されるまで依存関係を初期化する必要はありません

  • 依存関係が存在する場合はキャッシュされたコピーから依存関係をロードしたり、存在しない場合は再初期化したりできます(たとえば、JavaでSoftReferenceを使用)。

このような明らかに高度な手法には欠点があります。この場合の主な問題は、コードが不明瞭になり(コードで使用されるクラスが抽象化され、それらの明確な具体的な実装がないため、慣れていないと混乱する可能性があります)、依存性が高まることです。依存関係注入フレームワーク(もちろん、手動でオブジェクトをインスタンス化することは可能ですが、他の注入スタイルの場合よりも困難です)。

2
Jules