誰かが私に連絡して、依存性注入を概念的な方法で定義し、ソフトウェア設計でDIを使用することの実際の長所と短所を説明するように依頼されるたびに。私は、DIの概念を説明するのが難しいことを告白します。単一の責任の原則、継承に関する構成などについての歴史を伝える必要があるたびに。
開発者のためにDIを説明する最良の方法を説明してくれる人はいますか?
Dependency Injectionは恐ろしい名前です (IMO) 1 どちらかというと単純明快なコンセプトです。次に例を示します。
DbContext
など)を作成および管理します。この内部リソースは、依存関係と呼ばれるものですDbContext
)の作成と管理をメソッドから削除し、(メソッドのパラメーターとして、またはクラスのインスタンス化時に)このリソースを提供することを呼び出し側の責任にする
[1]:私は下位レベルのバックグラウンドを持っています。名前がDLL Injection。 Visual Studio(および私たち一般の開発者)が、プロジェクトが依存する.NETライブラリ(DLL、またはassemblies)を参照するという事実は、dependenciesはまったく役に立ちません。 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;
}
}
DbContext
をインスタンス化してクラスに(errm、inject)を渡すのは呼び出し側の責任です。
using ( var context = new DbContext("name=MyEntities") )
{
var repository = new RepositoryWithDI(context);
// work
}
多くの場合、抽象的な概念は実世界のアナロジーを使用してより適切に説明されます。これは私の頼りになる類推です:
あなたはサンドイッチ店を経営しています。あなたは素晴らしいサンドイッチを作りますが、パン自体についてはほとんど何も知りません。淡白なパンしかありません。あなたはパンをサンドイッチに変えるために使用するトッピングに完全に焦点を当てています。
ただし、お客様の中には茶色のパンを好む方もいます。全粒穀物を好む人もいます。どちらの方法でもかまいません。同じようなサイズのパンであれば、素晴らしいサンドイッチを作ることができます。また、いくつかの種類のパンを調達し、在庫を維持するという追加の責任を負う必要はありません。いくつかの種類のパンを在庫している場合でも、合理的には予測できないパンのエキゾチックな味わいを持つ顧客が常に存在します。
だからあなたは新しいルールを制定します:顧客は自分のパンを持ってきます。あなたはもはや自分でパンを提供しません。これは双方にメリットのある状況です。顧客は希望どおりのパンを手に入れることができ、不要なパンを調達する手間を省くことができます。結局のところ、あなたはサンドイッチメーカーであり、パン屋ではありません。
ああ、自分のパンを購入したくない顧客に対応するために、隣に2つ目のお店を開き、オリジナルの淡白なパンを販売しています。自分のパンを持参しなかった顧客は、デフォルトのパンを手に入れ、それからサンドイッチを作るためにあなたのところに来なければなりません。
完璧ではありませんが、重要な機能コンシューマに制御を与えるを強調しています。固有のwin-winは、独自の依存関係を取得する必要がなくなり、依存関係の選択に消費者が妨げられないことです。
それに対する簡単な答え:
まず、クラスには明確に定義された責任があり、このスコープの外にあるものはすべてそのクラスの外に保持する必要があります。これを踏まえると、依存性注入とは、「サードパーティ」の助けを借りて他のクラスBからクラスAに機能を注入し、このような懸念の分離を実現し、クラスAがそのスコープ外の操作を完了できるようにすることです。
.Net Coreは、このフレームワークが多くの依存性注入を使用するため、かなり良い例です。通常、挿入するサービスはstartup.cs
ファイルにあります。
確かに、学生はポリモーフィズム、インターフェース、およびOOP設計原則などのいくつかの概念に注意する必要があります。
これを正しく行うには、まず依存関係と注入を定義する必要があります。
基本的な例は、2つの値を加算するメソッドです。明らかに、このメソッドには値を追加する必要があります。それらが引数として渡されることによって提供される場合、これはすでに依存性注入のケースになります。代わりの方法は、プロパティまたはグローバル変数としてオペランドを実装することです。そうすれば、依存関係は注入されず、依存関係は外部から事前に利用可能になります。
代わりにプロパティを使用して、それらにAとBという名前を付けたとします。名前をOp1とOp2に変更すると、Addメソッドが壊れます。または、あなたのIDEはすべての名前を更新しますが、外部リソースへの依存関係があるため、メソッドも更新する必要があるということです。
この例は基本的なものですが、メソッドが画像などのオブジェクトに対して操作を実行する、またはファイルストリームから読み取る、より複雑な例を想像できます。メソッドがイメージに到達できるようにして、イメージがどこにあるかを知るように要求しますか?いいえ。メソッドでファイル自体を開き、ファイルを探す場所を知っているか、ファイルから読み取るかどうかを知るように要求しますか?番号。
ポイント:メソッドの機能をコアの動作に減らし、メソッドを環境から切り離します。あなたは2番目を行うことによって最初のものを得ます、あなたはこれを依存性注入の定義と考えるかもしれません。
利点:メソッドの環境に対する依存関係が排除されているため、メソッドへの変更は環境に影響を与えず、その逆も同様です。 =>アプリケーションの保守(変更)が容易になります。
本質的にシンプルなコンセプトの周りには、たくさんの綿毛と沈下があります。
コードで非常に簡単に実行できる場合、「使用するフレームワーク」に悩まされるのも非常に簡単です。
これは私が個人的に使用する定義です:
指定された動作XにYの依存関係があります。依存関係の注入には、Yのインスタンスがないという条件を満たしていても、Yのインスタンスであるという条件を満たす任意のYを提供する機能が含まれます。
例として、Yがファイルシステムまたはデータベース接続である場合があります。
moq などのフレームワークでは、ダブル(Yのふりをしたバージョン)を interface を使用して定義できるため、Yのインスタンスに注入できます。データベース接続。
これは純粋に単体テストの問題であると信じるのは簡単ですが、変更が予想されるコードのビットにとっては非常に有用なパターンであり、間違いなく良い習慣です。
パラメーターを介して関数にその動作を挿入する方法により、実行時の関数の動作を提供します。
Strategy Pattern は、依存性注入の優れた例です。