346

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

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

コンストラクター インジェクションの使用例:

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

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

  //...
}

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

Service Locator の使用例:

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

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

  //...
}

依存関係はオブジェクト自体にすぎないため、これらの依存関係には依存関係があり、さらに多くの依存関係が存在します。このようにして、コントロールコンテナ(またはDIコンテナ)の反転が生まれました。例: Castle Windsor、Ninject、Structure Map、Spring など)

しかし、IOC/DI コンテナはサービス ロケータとまったく同じように見えます。DIコンテナと呼ぶのは悪い名前ですか?IOC/DI コンテナはサービス ロケータの一種ですか? 多くの依存関係がある場合に主に DI コンテナーを使用するというニュアンスはありますか?

4

16 に答える 16

217

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

于 2009-10-13T01:24:55.420 に答える
108

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

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

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

于 2009-10-13T01:22:55.783 に答える
56

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

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

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

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

于 2009-10-13T01:26:00.877 に答える
47

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

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

つまり、サービスロケーターと依存性注入は、依存性逆転の原則の単なる実装です。

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

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

この投稿を確認できます:依存性逆転:サービスロケーターまたは依存性注入

また、古典的:制御の反転とMartinFowlerによる依存性注入パターン

ラルフ・E・ジョンソンとブライアン・フットによる再利用可能なクラスの設計

しかし、私の目を開いたのは次のとおりです。ASP.NET MVC:解決または注入?それが問題です…ディノ・エスポシート

于 2012-04-24T17:30:47.587 に答える
27

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

ただし、DI と SL は連携できます。共通の依存関係 (設定、ロガーなど) の中心的な場所があると便利です。そのような deps を使用するクラスを指定すると、deps を受け取る「実際の」コンストラクターと、SL から取得して「実際の」コンストラクターに転送するデフォルト (パラメーターなし) コンストラクターを作成できます。

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

于 2009-10-14T17:15:54.547 に答える
17

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

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

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

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

于 2015-02-22T21:03:33.293 に答える
8

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

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

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

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

于 2015-08-03T19:33:13.033 に答える
7

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

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

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

コンストラクターがIoCコンテナーに移動して依存関係を取得する、2番目の例では、のインスタンスがアプリの実際のランタイム構成でFoo正しくインスタンス化できることをテストできる唯一の方法は、実際に構築することです。それ

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

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

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);

この例を参照してください)。

テスト時にアプリケーションのコンポジションルートを確認することで、プロセスの後半でテストをすり抜ける可能性のあるエラーを検出できる可能性があります。

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

于 2012-07-03T05:13:25.073 に答える
6

I think the two work together.

Dependency injection means you push in some dependant class/interface to a consuming class (usually to it's constructor). This decouples the two classes via an interface and means the consuming class can work with many types of "injected dependency" implementations.

The role of the service locator is to pull together your implementation. You setup a service locator via some boot strapping at the start of your program. Bootstrapping is the process of associating a type of implementation to a particular abstract/interface. Which gets created for you at run time. (based on you config or bootstrap). If you hadn't implemented dependency injection, it would be very difficult to utilise a service locator or IOC container.

于 2011-02-19T14:17:22.403 に答える
5

注:質問に正確に答えているわけではありません。しかし、たまたまこのページにたどり着いたService Locator (アンチ) パターンと混同している依存性注入パターンの新しい学習者にとって、これは役立つと思います。

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

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

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

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

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

于 2012-01-10T08:07:53.977 に答える
1

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

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

于 2017-01-23T11:30:59.117 に答える
-3

記録のために

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

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

  //...
}

インターフェイスが本当に必要な場合 (インターフェイスが複数のクラスで使用されている場合) を除き、 MUST NOT USE IT を使用してください。この場合、IBar はそれを実装する任意のサービス クラスを利用できます。ただし、通常、このインターフェイスは単一のクラスで使用されます。

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

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

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

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

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

  //...
}

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

于 2018-09-30T00:47:21.287 に答える