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