web-dev-qa-db-ja.com

Dependency InjectionとService Locatorのパターンの違いは何ですか?

両方のパターンは、制御の反転の原則の実装のように見えます。つまり、オブジェクトは依存関係を構築する方法を知らない必要があります。

依存性注入(DI)は、コンストラクターまたはセッターを使用して依存性を「注入」するようです。

コンストラクター注入の使用例:

//Foo Needs an IBar
public class Foo
{
  private IBar bar;

  public Foo(IBar bar)
  {
    this.bar = bar;
  }

  //...
}

Service Locatorは「コンテナ」を使用しているようで、依存関係を結び付けてfooにバーを提供します。

サービスロケーターの使用例:

//Foo Needs an IBar
public class Foo
{
  private IBar bar;

  public Foo()
  {
    this.bar = Container.Get<IBar>();
  }

  //...
}

依存関係は単なるオブジェクトなので、これらの依存関係には依存関係があり、さらに依存関係があります。このようにして、Inversion of Control Container(またはDI Container)が生まれました。例:Castle Windsor、Ninject、Structure Map、Springなど)

ただし、IOC/DIコンテナはexactly Service Locatorのように見えます。それをDIコンテナと呼ぶのは悪い名前ですか? IOC/DIコンテナーは、Service Locatorの別のtypeですか?多くの依存関係がある場合、DIコンテナを使用するという事実のニュアンスはありますか?

266
Charles Graham

違いはわずかに見えるかもしれませんが、ServiceLocatorを使用しても、クラスは依存関係を作成する責任があります。サービスロケーターを使用して実行します。 DIでは、クラスにその依存関係が与えられます。それは、彼らがどこから来たかを知らず、気にしません。これの重要な結果の1つは、DIの例の方が単体テストがはるかに簡単なことです。依存オブジェクトのモック実装を渡すことができるためです。この2つを組み合わせて、必要に応じてサービスロケーター(またはファクトリー)を挿入できます。

162
tvanfosson

サービスロケーターを使用する場合、すべてのクラスはサービスロケーターに依存します。これは、依存性注入の場合には当てはまりません。通常、依存関係インジェクターは、起動時に1回だけ呼び出されて、いくつかのメインクラスに依存関係を注入します。このメインクラスが依存するクラスには、完全なオブジェクトグラフが作成されるまで、依存関係が再帰的に挿入されます。

良い比較: http://martinfowler.com/articles/injection.html

依存性インジェクターがサービスロケーターのように見え、クラスがインジェクターを直接呼び出す場合、おそらく依存性インジェクターではなく、サービスロケーターです。

81
Joel

サービスロケーターは依存関係を隠します-ロケーターから接続を取得するときに、オブジェクトがデータベースにヒットするかどうかを判断することはできません(たとえば)。依存性注入(少なくともコンストラクター注入)では、依存性は明示的です。

さらに、サービスロケーターは、他のオブジェクトの依存関係へのグローバルアクセスポイントを提供するため、カプセル化を解除します。サービスロケーターを使用する場合、- シングルトンと同様

その実装の動作が外部から干渉される可能性があるため、クライアントオブジェクトのインターフェイスの事前条件と事後条件を指定することが困難になります。

依存関係注入では、オブジェクトの依存関係が指定されると、オブジェクト自体の制御下に置かれます。

44
Jeff Sternal

マーティン・ファウラーは述べています

サービスロケーターを使用すると、アプリケーションクラスはロケーターへのメッセージによって明示的に要求します。インジェクションでは、明示的な要求はなく、サービスはアプリケーションクラスに表示されます。したがって、制御の反転です。

要するに、Service LocatorとDependency Injectionは、依存関係反転の原理の単なる実装です。

重要な原則は「コンクリートにではなく、抽象に依存する」です。これにより、ソフトウェア設計が「疎結合」、「拡張可能」、「柔軟」になります。

ニーズに最適なものを使用できます。巨大なコードベースを持つ大規模なアプリケーションの場合、依存関係の注入によりコードベースをさらに変更する必要があるため、Service Locatorを使用することをお勧めします。

この投稿を確認できます: Dependency Inversion:Service Locator or Dependency Injection

また、古典: コントロールコンテナーの反転とMartin Fowlerによる依存性注入パターン

再利用可能なクラスの設計 by Ralph E. Johnson&Brian Foote

しかし、私の目を開いたのは: ASP.NET MVC:解決するか注入するか?それが問題です... by Dino Esposito

34
Nathan

コンストラクターDIを使用するクラスは、消費するコードに、満たすべき依存関係があることを示します。クラスがそのような依存関係を取得するために内部的にSLを使用する場合、消費するコードは依存関係を認識しません。これは表面的には良く見えるかもしれませんが、明示的な依存関係を知ることは実際に役立ちます。アーキテクチャの観点からはより優れています。また、テストを行う場合、クラスに特定の依存関係が必要かどうかを把握し、それらの依存関係の適切な偽バージョンを提供するようにSLを構成する必要があります。 DIでは、偽物を渡すだけです。大きな違いではありませんが、そこにあります。

ただし、DIとSLは一緒に機能します。共通の依存関係(たとえば、設定、ロガーなど)を中央に配置すると便利です。そのようなdepsを使用するクラスを指定すると、depを受け取る「実際の」コンストラクターと、SLから取得して「実際の」コンストラクターに転送するデフォルト(パラメーターなし)コンストラクターを作成できます。

編集:そして、もちろん、SLを使用すると、そのコンポーネントに何らかのカップリングが導入されます。これは皮肉なことです。そのような機能の概念は抽象化を促進し、結合を減らすことだからです。懸念はバランスを取ることができ、SLを使用する必要がある場所の数に依存します。上記の提案どおりに行う場合は、デフォルトのクラスコンストラクターで行います。

20
Grant Palin

私の最後のプロジェクトでは、両方を使用しています。ユニットのテスト容易性のために依存性注入を使用します。サービスロケーターを使用して実装を隠し、IoCコンテナーに依存しています。はい! IoCコンテナ(Unity、Ninject、Windsor Castle)のいずれかを使用すると、それに依存します。そして、それが古くなったり、何らかの理由で交換したい場合は、少なくとも構成ルートを変更する必要があります。ただし、サービスロケーターはそのフェーズを抽象化します。

IoCコンテナーに依存しない方法独自にラップする必要があります(これは悪い考えです)か、Service Locatorを使用してIoCコンテナーを構成します。したがって、必要なインターフェイスを取得するようにサービスロケーターに指示し、そのインターフェイスを取得するように構成されたIoCコンテナーを呼び出します。

私の場合、フレームワークコンポーネントである ServiceLocator を使用します。そして、IoCコンテナには nity を使用します。将来、IoCコンテナーを Ninject に交換する必要がある場合は、UnityではなくNinjectを使用するようにサービスロケーターを構成する必要があります。簡単な移行。

このシナリオを説明する素晴らしい記事があります。 http://www.johandekoning.nl/index.php/2013/03/03/dont-wrap-your-ioc-container/

6
Teoman shipahi

この2つは一緒に機能すると思います。

依存性注入とは、いくつかの依存クラス/インターフェースを消費クラスにプッシュすることを意味します(通常はコンストラクターに)。これにより、インターフェースを介して2つのクラスが分離され、消費するクラスが多くのタイプの「注入された依存関係」実装で動作できることを意味します。

サービスロケーターの役割は、実装をまとめることです。プログラムの開始時にブートストラップを使用してサービスロケーターをセットアップします。ブートストラップは、特定の抽象/インターフェースに実装のタイプを関連付けるプロセスです。実行時に作成されます。 (設定またはブートストラップに基づきます)。依存性注入を実装していない場合、サービスロケーターまたはIOCコンテナーを利用することは非常に困難です。

5
NoelAdy

追加する理由の1つは、先週MEFプロジェクト用に作成したドキュメントの更新に触発されました(MEFの作成を支援します)。

アプリが数千のコンポーネントで構成される場合、特定のコンポーネントを正しくインスタンス化できるかどうかを判断するのは困難です。 「正しくインスタンス化された」とは、この例ではFooコンポーネント、IBarのインスタンスに基づいており、利用可能になること、およびそれを提供するコンポーネントが次のことを行うことを意味します。

  • 必要な依存関係がある、
  • 無効な依存関係サイクルに関与しない
  • mEFの場合、1つのインスタンスのみが提供されます。

コンストラクターが依存関係を取得するためにIoCコンテナーに移動する2番目の例では、Fooのインスタンスが正しくインスタンス化できることをテストできる唯一の方法アプリの実際の実行時設定で実際に構築します.

実行時に機能するコードはテストハーネスでは必ずしも機能しないため、これにはテスト時のあらゆる種類の厄介な副作用があります。実際の構成はテスト時に必要なものであり、テスト時のセットアップではないため、モックは行いません。

この問題の根本は、@ Jonによって既に指摘されている違いです。コンストラクターを介した依存関係の注入は宣言的であり、2番目のバージョンでは命令型のService Locatorパターンを使用します。

IoCコンテナを慎重に使用すると、関連するコンポーネントのインスタンスを実際に作成することなく、アプリのランタイム構成を静的に分析できます。多くの一般的なコンテナは、このバリエーションを提供します。 Microsoft.Compositionは、.NET 4.5 WebおよびMetroスタイルアプリを対象とするMEFのバージョンであり、WikiドキュメントでCompositionAssertサンプルを提供します。それを使用して、次のようなコードを書くことができます。

 // Whatever you use at runtime to configure the container
var container = CreateContainer();

CompositionAssert.CanExportSingle<Foo>(container);

この例 を参照)。

テスト時にアプリケーションのComposition Rootsを検証することで、プロセスの後半でテストをすり抜ける可能性のあるエラーを潜在的にキャッチできます。

これが、トピックに関する包括的な回答セットの興味深い追加であると思います!

5

どちらもIoCの実装テクニックです。 Inversion of Controlを実装する他のパターンもあります。

  • 工場パターン
  • サービスロケーター
  • 依存性注入(コンストラクター注入、パラメーター注入(必要でない場合)、インターフェース注入のセッター注入)...

サービスロケーターとDIはより類似しているようです。どちらもコンテナーを使用して依存関係を定義し、抽象化を具体的な実装にマップします。

主な違いは、依存関係がどのように配置されるか、Service Locationクライアントコードが依存関係を要求する、DIではコンテナーを使用してすべてのオブジェクトを作成し、コンストラクターパラメーター(またはプロパティ)として依存関係を注入することです。

5
Nininea

この単純化しすぎた場合には違いはなく、それらは同じ意味で使用できます。ただし、現実の問題はそれほど単純ではありません。 Barクラス自体にDという名前の別の依存関係があると仮定してください。その場合、サービスロケーターはその依存関係を解決できず、Dクラス内でインスタンス化する必要があります。依存関係をインスタンス化するのはクラスの責任だからです。 Dクラス自体に他の依存関係がある場合はさらに悪化し、実際の状況では通常、それよりもさらに複雑になります。このようなシナリオでは、DIはServiceLocatorよりも優れたソリューションです。

3
Daniel

注:質問に正確に答えているわけではありません。しかし、これは Service Locator(anti-)pattern と混同しているDependency Injectionパターンの新しい学習者にとって、このページに偶然出くわしたときに役立つと思います。

Service Locator(現在はアンチパターンと見なされているようです)とDependency Injectionパターンの違いを知っており、各パターンの具体的な例を理解できますが、コンストラクター内のサービスロケーターを示す例に混乱しました(仮定します)コンストラクターインジェクションを再実行してください)。

「サービスロケーター」は、多くの場合、パターンの名前としても、new演算子を使用せずにオブジェクトを取得するためにそのパターンで使用されるオブジェクトを参照する名前としても使用されます。現在、同じタイプのオブジェクトを composition root で使用して、依存関係の注入を実行することもできます。これが混乱の原因です。

ポイントは、DIコンストラクター内でサービスロケーターオブジェクトを使用している可能性がありますが、「サービスロケーターパターン」を使用していないことです。代わりにIoCコンテナーオブジェクトとして参照する場合、混乱が少なくなります。これらは基本的に同じことを行うと推測されるかもしれません(間違っている場合は修正してください)。

サービスロケーター(または単にロケーター)と呼ばれるか、IoCコンテナー(または単にコンテナー)と呼ばれるかどうかは、同じ抽象化を参照している可能性があるため、推測どおりの違いはありません(間違っている場合は修正してください) )。それをサービスロケーターと呼ぶことは、依存関係注入パターンと共にService Locatorアンチパターンを使用していることを示唆しているだけです。

私見では、「ロケーション」または「ロケーティング」ではなく「ロケーター」と名付けているため、記事内のサービスロケーターがサービスロケーター(アンチ)パターンではなく、サービスロケーターコンテナを参照していると考えることがあります、特にDependency InjectorではなくDependency Injectionと呼ばれる関連パターンがある場合。

3
blizpasta

Dependency InjectionとService Locatorの違い(ある場合)は何ですか?両方のパターンは、依存関係反転の原則を実装するのに適しています。 Service Locatorパターンは、パブリックインターフェイスを強制的に変更することなく全体的な設計を緩和するため、既存のコードベースで使用する方が簡単です。これと同じ理由で、Service Locatorパターンに基づくコードは、Dependency Injectionに基づく同等のコードよりも読みにくくなります。

Dependency Injectionパターンは、クラス(またはメソッド)がどの依存関係を持つかを示す署名を明確にします。このため、結果のコードはよりクリーンで読みやすくなります。

1
Yogesh