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


304

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

Dependency Injection(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コンテナはサービスロケータとまったく同じように見えます。それをDIコンテナーと呼ぶのは悪い名前ですか?IOC / DIコンテナは別のタイプのサービスロケータですか?多くの依存関係がある場合にDIコンテナーを使用するという事実のニュアンスはありますか?


13
制御の反転とは、「オブジェクトはその依存関係を構築する方法を知ってはならない」ことを意味します?!?それは私にとって新しいものです。いいえ、実際には、それは「制御の反転」が意味するものではありません。martinfowler.com/bliki/InversionOfControl.htmlを参照してくださいこの記事では、1980年代にさかのぼる用語の語源についてのリファレンスも提供しています。
–Rogério、


1
Mark SeemannはService Locatorをアンチパターンとして主張しています(blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern)。また、DIおよびSLの問題を理解するのに役立つ図(ここにある stackoverflow.com/a/9503612/1977871)も役に立ちました。お役に立てれば。
VivekDev 2016年

回答:


181

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


20
さらに、クラスを構築するときに両方を使用できます。デフォルトのコンストラクターは、SLを使用して依存関係を取得し、それらを依存関係を受け取る「実際の」コンストラクターに渡すことができます。あなたは両方の世界のベストを手に入れます。
Grant Palin、

6
いいえ、ServiceLocatorは、特定の依存関係(プラグイン)の正しい実装をインスタンス化する役割を果たします。DIの場合、DI「コンテナ」がその責任を負います。
ロジェリオ2010

5
@ロジェリオはい、しかしクラスはまだサービスロケーターについて知らなければなりません...それは2つの依存関係です。また、サービスロケータが、特にサービスサポートを必要とする一時的なオブジェクトのルックアップのためにDIコンテナにデリゲートすることもよくあります。
アダム・ゲント

2
@Adam Service LocatorがDIコンテナーに委任するとは言いませんでした。「公式」記事で説明されているように、これらは2つの相互に排他的なパターンです。私には、Service LocatorはDIに比べて実際に大きな利点があります。DIコンテナーを使用すると、悪用を招きます(私は繰り返し見ました)が、Service Locatorを使用するとそうではありません。
ホジェリオ

3
「これの重要な結果の1つは、DIの例の方が単体テストがはるかに簡単になることです。依存するオブジェクトの実装のモックを渡すことができるためです。」違います。単体テストでは、サービスロケーターコンテナー内の登録関数の呼び出しを使用して、モックをレジストリに簡単に追加できます。
Drumbeg 2014年

93

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

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

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


17
しかし、実行時にオブジェクトを作成する必要がある場合はどのように処理しますか?「新規」で手動で作成すると、DIを利用できません。ヘルプのためにDIフレームワークを呼び出すと、パターンが崩れます。それでは、どのようなオプションが残っていますか?
ボリス

9
@Boris同じ問題があり、クラス固有のファクトリを注入することにしました。きれいではありませんが、仕事を成し遂げました。きれいなソリューションを見たいです。
チャーリーRudenstål2014年


2
@ボリスオンザフライで新しいオブジェクトを作成する必要がある場合、そのオブジェクトに抽象ファクトリを注入します。これは、この場合のサービスロケータの挿入に似ていますが、関連するオブジェクトを構築するための具体的で統一されたコンパイル時のインターフェイスを提供し、依存関係を明示的にします。
LivePastTheEnd 2017

51

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

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

クライアントオブジェクトのインターフェイスの事前条件と事後条件を指定するのは難しくなります。これは、その実装の仕組みを外部から操作できるためです。

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


3
私は「シングルトンはバカだと思った
チャールズグラハム

2
私は昔のSteve Yeggeが大好きで、その記事のタイトルは素晴らしいですが、私が引用した記事とMiškoHeveryの「Singletons is Pathological liars」(misko.hevery.com/2008/08/17/singletons-are-pathological-嘘つき)は、サービスロケーターの特定の悪魔に対してより良い判決を下します。
ジェフスターナル

この回答が最も正しいのは、サービスロケーターを最もよく定義しているためです:「その依存関係を隠すクラス」。内部的に依存関係を作成しても、多くの場合良いことではありませんが、クラスをサービスロケーターにすることはできません。また、コンテナへの依存関係を取得することは問題ですが、サービスロケータを最も明確に定義する「その」問題ではありません。
サム

1
With dependency injection (at least constructor injection) the dependencies are explicit.。説明してください。
FindOutIslamNow

上記のように、私は... SLはDIよりも依存性が少ない明示的になりどのように見ることができない
のMichałPowłoka

38

マーティンファウラーは次のように述べています

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

つまり、Service LocatorとDependency Injectionは、Dependency Inversion Principleの単なる実装です。

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

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

あなたはこの投稿をチェックすることができます:依存関係の逆転:Service LocatorまたはDependency Injection

また、クラシック:制御コンテナの反転とMartin FowlerによるDependency Injectionパターン

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

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


素晴らしい要約:「Service LocatorとDependency InjectionはDependency Inversion Principleの単なる実装です。」
Hans

また、彼は次のようにも述べています。主な違いは、サービスロケーターでは、サービスのすべてのユーザーがロケーターへの依存関係を持っていることです。ロケーターは他の実装への依存関係を隠すことができますが、ロケーターを確認する必要があります。したがって、ロケータとインジェクタの間の決定は、その依存関係が問題であるかどうかに依存します。
programaths

1
ServiceLocatorとDIは、「依存関係の逆転の原則」(DIP)とは関係ありません。DIPは、低レベルコンポーネントへのコンパイル時の依存関係を、高レベルコンポーネントとともに定義された抽象型への依存関係で置き換えることにより、高レベルコンポーネントをより再利用可能にする方法です。レベルコンポーネント; このようにして、コンパイル時の依存関係が反転します。これは、高レベルのものに依存するのが低レベルのものだからです。また、Martin Fowlerの記事では、DIとIoCは同じものではないことを説明してます。
ホジェリオ

23

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

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

編集:そしてもちろん、SLを使用する場合、そのコンポーネントへの結合を導入します。皮肉なのは、そのような機能の概念が抽象化を促進し、結合を減らすことであるからです。懸念はバランスを取ることができ、SLを使用する必要がある場所の数によって異なります。上記のように行われた場合は、デフォルトのクラスコンストラクターでのみ。


面白い!DIとSLの両方を使用していますが、2つのコンストラクターは使用していません。3つまたは4つの最も退屈な頻繁に必要な依存関係(設定など)は、オンザフライでSLから取得されます。その他はすべてコンストラクター注入です。少し見苦しいですが、実用的です。
maaartinus 2017年

10

どちらもIoCの実装手法です。制御の反転を実装する他のパターンもあります。

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

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

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


7

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

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

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

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


johandekoning記事のリンクが壊れています。
JakeJ 2018年

6

この2つは連携していると思います。

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

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


6

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

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

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

コンストラクタはその依存関係を取得するためにIoCコンテナに行くあなたが与えた第二の例では、あなたがのインスタンスがあることをテストする唯一の方法Fooを正しくインスタンス化することができるようになりますあなたのアプリケーションの実際の実行時設定では、することです実際に構築しますそれ

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

この問題の原因は、@ Jonによってすでに呼び出されている違いです。2番目のバージョンでは命令型のサービスロケーターパターンが使用されていますが、コンストラクターを介した依存関係の注入は宣言型です。

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

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

CompositionAssert.CanExportSingle<Foo>(container);

(見る この例を)。

を検証することによって テスト時にアプリケーションの構成ルートを、プロセスの後の段階でテストをすり抜ける可能性のあるエラーを見つけることができます。

これがトピックに関するこの他の包括的な回答セットへの興味深い追加であることを願っています!


5

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

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

"Service Locator"は、パターンの名前と、そのパターンで使用されるオブジェクトを参照するための名前(両方とも想定)の両方として使用され、new演算子を使用せずにオブジェクトを取得します。これで、同じタイプのオブジェクトをコンポジションルートでも使用できます依存性注入を実行する、混乱が生じます。

重要なのは、DIコンストラクタ内でサービスロケータオブジェクトを使用している可能性があるが、「サービスロケータパターン」を使用していないことです。本質的に同じことをしていると思っているかもしれませんが、IoCコンテナーオブジェクトとして参照する方が混乱しにくいです(間違っている場合は修正してください)。

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

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


4

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


4
うーん、そうは思いません。サービスロケータexです。依存関係がまだあることを明確に示しています...サービスロケータ。barクラス自体に依存関係がある場合は、barサービスロケーターも含まれます。これがDI / IoCを使用する全体のポイントです。
GFoley83 2014年

2

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

Dependency Injectionパターンは、クラス(またはメソッド)がどの依存関係を持つかというシグネチャを明確にします。このため、結果のコードはより簡潔で読みやすくなっています。


1

単純な概念に従うことで、Service LocatorとDIコンテナーの違いをより明確に理解できました。

  • Service Locatorはコンシューマーで使用され、直接のコンシューマーの要求により、ストレージからIDでサービスを取得します

  • DIコンテナーは外部のどこかにあり、ストレージからサービスを取得してコンシューマーにプッシュします(コンストラクターまたはメソッドを介して)

ただし、これらの違いについては、具体的な消費者の使用状況でのみ説明できます。Service LocatorとDIコンテナーをコンポジションルートで使用する場合、それらはほとんど同じです。


0

DIコンテナーは、サービスロケーターのスーパーセットです。これは、依存関係の注入を組み立てる(配線する)追加機能とともに、サービス見つけるために使用できます。


-2

記録のために

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

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

  //...
}

本当にインターフェースが必要でない限り(インターフェースは複数のクラスで使用されます)、それを使用してはいけません。この場合、IBarでは、それを実装するサービスクラスを利用できます。ただし、通常、このインターフェイスは単一のクラスによって使用されます。

インターフェースを使用するのが悪いのはなぜですか?デバッグが本当に難しいからです。

たとえば、インスタンス「バー」が失敗したとしましょう、質問:どのクラスが失敗しましたか?。 どのコードを修正する必要がありますか? 単純なビュー、それはインターフェースにつながり、私の道はここで終わります。

代わりに、コードがハード依存関係を使用している場合、間違いをデバッグするのは簡単です。

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

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

  //...
}

「bar」が失敗した場合は、BarServiceクラスを確認して実行する必要があります。


1
クラスは、特定のオブジェクトを構築するための青写真です。一方、インターフェースはでcontractあり、アクションではなく動作を定義するだけです。実際のオブジェクトを渡すのではなく、インターフェイスのみが共有されるため、コンシューマーはオブジェクトの残りの部分にアクセスできません。また、単体テストでは、テストが必要な部分のみをテストするのに役立ちます。あなたはその有用性を理解するでしょう。
Gunhan
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.