依存性注入の最良の定義は何ですか?


10

誰かが私に連絡して、依存性注入を概念的な方法で定義し、ソフトウェア設計でDIを使用することの実際の長所と短所を説明するように求められるたびに。DIの概念を説明するのが難しいと告白します。単一の責任の原則、継承に関する構成などに関する歴史を伝える必要があるたびに。

開発者のためにDIを説明する最良の方法を説明してくれる人はいますか?


2
ここでの課題は、DIの定義が非常に矛盾していることです。私は「純粋なDI」というスタンスをとっています。すべての状態やデータなどを提供するためにパラメーターに依存する関数がある場合、その関数はDIを使用しています。反対の極端な例として、DIフレームワークがないと依存関係の注入はないと主張する人もいます(もちろん、それらは間違いです;))。したがって、定義を明確にできない限り、それが何であるかを説明し始めることはできません...
David Arno

だから、私が理解しているように、これは私の問題だけではありません。
ティアゴサンパイオ



依存関係の注入は、依存関係の逆転を実現するために使用される1つの手法です。他のすべては、その上に構築された余分なものです。これらの2つの用語では、「依存関係」という単語の意味が少し異なることに注意してください。依存性注入では、コードが依存するコンポーネントを指します。依存関係の逆転では、それは(指示された)関係自体を指します-逆にしたい関係 後者が目標なので、主な長所と短所は同じです。さらに、オブジェクトのライフタイム管理など、実際の実装に関連する追加の懸念事項。
FilipMilovanović19年

回答:


22

Dependency Injectionは、かなり単純な概念の恐ろしい名前(IMO)1です。次に例を示します。

  1. Xを実行するメソッド(またはメソッドを含むクラス)がある(データベースからデータを取得するなど)
  2. Xの実行の一部として、このメソッドは内部リソース(例:)を作成および管理しDbContextます。この内部リソースは、いわゆる依存関係です
  3. リソース(つまりDbContext)の作成と管理をメソッドから削除し、呼び出し側がこのリソースを(メソッドパラメータとして、またはクラスのインスタンス化時に)提供するようにします。
  4. 依存関係の注入を実行しています。


[1]:私は下位レベルのバックグラウンドを持っています。名前がそれがDLLインジェクションのようなはるかに複雑なものであることを意味するため、座って依存性インジェクションを学ぶのに何ヶ月もかかりました。Visual Studio(および開発者一般)は、依存関係がまったく役に立たないため、プロジェクトが依存する.NETライブラリ(DLL、またはアセンブリ)を参照するという事実。Dependency Walker(depends.exe)のようなものさえあります。


[編集]いくつかのデモコードが便利になると思ったので、これを(C#で)示します。

依存性注入なし:

public class Repository : IDisposable
{
    protected DbContext Context { get; }

    public Repository()
    {
        Context = new DbContext("name=MyEntities");
    }

    public void Dispose()
    {
        Context.Dispose();
    }
}

次に、消費者は次のようなことを行います。

using ( var repository = new Repository() )
{
    // work
}

依存性注入パターンで実装された同じクラスは、次のようになります。

public class RepositoryWithDI
{
    protected DbContext Context { get; }

    public RepositoryWithDI(DbContext context)
    {
        Context = context;
    }
}

aをインスタンス化してクラスにDbContext渡す(errm、injectする)のは呼び出し側の責任です。

using ( var context = new DbContext("name=MyEntities") )
{
    var repository = new RepositoryWithDI(context);

    // work
}

3
これはウィキペディアに追加する必要があります。
Evorlor

2
DbContextをインスタンス化するのは呼び出し側の責任です。これは、必要なすべての依存関係をインスタンス化するアプリケーションのエントリポイントの責任だと思います。したがって、消費者は必要なタイプを独自の契約の依存関係として導入するだけで済みます。
Fabio

@ファビオかもしれません。(その場合、呼び出し元の責任は、アプリケーションの起動時にインスタンス化されたリソースを、呼び出されるメソッド/クラスに提供することです。)私の例では、依存関係注入の概念を説明するための要件ではないため、そうではありません。 。
Marc.2377

5

多くの場合、抽象的な概念は実世界のアナロジーを使用してより適切に説明されます。これは私の比喩です:

あなたはサンドイッチ店を経営しています。あなたは素晴らしいサンドイッチを作りますが、パン自体についてはほとんど何も知りません。穏やかな白パンしかありません。あなたはパンをサンドイッチに変えるために使用するトッピングに完全に焦点を当てています。

ただし、お客様の中には茶色のパンを好む方もいます。全粒穀物を好む人もいます。どちらの方法でもかまいません。同様のサイズのパンであれば、素晴らしいサンドイッチを作ることができます。また、いくつかの種類のパンを調達し、在庫を維持するという追加の責任を負う必要はありません。いくつかの種類のパンを在庫していても、合理的には予測できないパンにエキゾチックな味がする顧客が常にいるでしょう。

あなたは新しいルールを制定します:顧客は自分のパンを持参します。あなたはもはや自分でパンを提供しません。これは双方にメリットのある状況です。顧客は希望どおりのパンを手に入れることができるので、不要なパンを調達する手間を省くことができます。結局のところ、あなたはサンドイッチメーカーであり、パン屋ではありません。

ああ、そして自分のパンを購入したくない顧客に対応するために、隣の2番目の店を開いて、オリジナルの淡白なパンを販売しています。自分のパンを持っていなかった顧客は、デフォルトのパンを手に入れ、それを使ってサンドイッチを作るためにあなたに来なければなりません。

完璧ではありませんが、主な機能である消費者へのコントロールの提供を強調しています。固有のwin-winは、独自の依存関係を取得する必要がなくなり、依存関係の選択に消費者が妨げられないことです。


1
私はこれが好きですが、OPは開発者向けの説明探しています。最初の抽象化は良いですが、遅かれ早かれ実際の例が必要になります。
ロビーディー

1
@RobbieDee:パターンの目的が明確である場合、その実装も明確になる傾向があります。たとえば、マルクの答えは完全に正しいですが、彼が使用する例示的な状況の複雑な性質に説明が行き詰まっているように感じます。これは、「船を建造したい場合は、人々を太鼓で集めて木材を集めたり、任務や仕事を割り当てたりせず、果てしない海の広大さを切望するように教える」ということになります。。何をすべきかを説明するのではなく、なぜそれを行うのを説明する方が好きです。
Flater

2
もちろんそうですが、私は手助けできず、具体的な例が必要だと思います。たとえば、食欲をそそる実際のファイルシステムやデータベースがないのですが、それはおそらく私の狭い開発者の見解です:)
ロビーディー

1

それに対する簡単な答え:

まず、クラスには明確に定義された責任があり、このスコープの外にあるものはすべてそのクラスの外に保持する必要があります。これを踏まえると、依存性注入とは、「サードパーティ」の助けを借りて他のクラスBからクラスAに機能を注入し、このような懸念の分離を実現し、クラスAがそのスコープ外の操作を完了できるようにすることです。

.Net Coreは、このフレームワークが多くの依存性注入を使用するため、かなり良い例です。通常、注入するサービスはstartup.csファイルにあります。

確かに、学生は、ポリモーフィズム、インターフェース、OOP設計原則などのいくつかの概念に注意する必要があります。


0

本質的には単純な概念であるものの周りには多くの綿毛と沈下があります。

また、コードで非常に簡単に実行できる場合、「使用するフレームワーク」に悩まされるのも非常に簡単です。

これは私が個人的に使用する定義です:

Yの依存関係を持つ動作Xを想定します。依存関係注入には、Yのインスタンスであるという基準を満たしていない場合でも、Yのインスタンスとなる基準を満たすYを提供する機能が含まれます。

いくつかの例は、Yがファイルシステムまたはデータベース接続である場合です。

ようなフレームワークMOQはダブルス(Yのふりバージョン)を使用して定義されることを可能にするインターフェースを、Yは、例えば、データベース接続であるYのインスタンスに注入することができるので。

これは純粋に単体テストの問題であると信じるのは簡単ですが、変更が予想されるコードのビットにとっては非常に有用なパターンであり、間違いなく良い習慣です。


0

パラメーターを介して関数にその動作を挿入する方法により、実行時の関数の動作を提供します。

戦略パターンは、依存性注入の優れた例です。


0

これを正しく行うには、まず依存関係と注入を定義する必要があります。

  • 依存関係:操作に必要なリソース。
  • インジェクション:通常、メソッドへの引数として、そのリソースをオペレーションに渡します。

基本的な例は、2つの値を加算するメソッドです。明らかに、このメソッドには値を追加する必要があります。それらが引数として渡されることによって提供される場合、これはすでに依存性注入のケースになります。代わりの方法は、プロパティまたはグローバル変数としてオペランドを実装することです。そうすれば、依存関係は注入されず、依存関係は外部から事前に利用可能になります。

代わりにプロパティを使用して、それらにAおよびBという名前を付けたとします。名前をOp1およびOp2に変更すると、Addメソッドが中断されます。または、IDEがすべての名前を更新します。要点は、外部リソースへの依存関係があるため、メソッドも更新する必要があるということです。

この例は基本的なものですが、メソッドが画像などのオブジェクトに対して操作を実行する、またはファイルストリームから読み取る、より複雑な例を想像できます。メソッドがイメージに到達できるようにして、イメージがどこにあるかを知るように要求しますか?いいえ。メソッドでファイル自体を開き、ファイルを探す場所を知っているか、ファイルから読み取るかどうかを知るように要求しますか?番号。

ポイント:メソッドの機能をコアの振る舞いに減らし、メソッドを環境から切り離すこと。あなたは2番目を行うことによって最初のものを得ます、あなたはこれを依存性注入の定義と考えるかもしれません。

利点:メソッドの環境の依存関係が排除されているため、メソッドへの変更は環境に影響を与えず、その逆も同様です。=>アプリケーションの保守(変更)が容易になります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.