依存性注入 についての具体的な質問と一緒に、いくつかの質問がすでに投稿されています。しかしながら、
依存性注入とは何ですか。また、いつ/なぜそれを使用すべきか、または使用すべきではありませんか。
基本的には、オブジェクトに依存関係を作成させたり、ファクトリオブジェクトに依存関係を作成させるのではなく、必要な依存関係を外部にオブジェクトに渡して、他の人の問題にします。この「誰か」は、依存関係グラフのさらに上位にあるオブジェクト、または依存関係グラフを作成する依存関係インジェクタ(フレームワーク)のいずれかです。ここで使用している依存関係は、現在のオブジェクトが参照を保持する必要があるその他のオブジェクトです。
依存性注入の主な利点の1つは、テストをずっと簡単にできることです。コンストラクタ内で次のような動作をするオブジェクトがあるとします。
public SomeClass() {
myObject = Factory.getObject();
}
特にSomeClass
が複雑なディスクまたはネットワークアクセスを行うものである場合は、myObject
上でいくつかの単体テストを実行するだけでよい場合、これは面倒になることがあります。それで今、あなたはモザイクmyObject
だけでなく、どういうわけかファクトリコールを傍受しているのを見ています。ハード。代わりに、オブジェクトをコンストラクタの引数として渡します。これで問題は別の場所に移動しましたが、テストははるかに簡単になります。ダミーのmyObject
を作成してそれを渡すだけです。コンストラクタは次のようになります。
public SomeClass (MyClass myObject) {
this.myObject = myObject;
}
これは、コンストラクタによる依存性注入の1つのスタイルです。いくつかのメカニズムが可能です。
依存性注入を使用しない場合(コンストラクタなどであまりにも多くの作業を行うクラスなど)、単体テストでコンポーネントを分離することははるかに困難になる傾向があります。
2013年に私がこの答えを書いたとき、これは Google Testing Blog の主要なテーマでした。ランタイム設計に特別な柔軟性を常に必要とするわけではないため(たとえば、サービスロケーターや類似のパターンなど)、これは私にとって最大の利点ですが、テスト中にクラスを分離できる必要があることがよくあります。
私がこれまで見つけた最高の定義は James Shoreによるものです :
「依存注射」とは、5セントの概念を表す25ドルの用語です。 [...] 依存性注入はオブジェクトにそのインスタンス変数を与えることを意味します。 [...].
Martin Fowlerによる記事 もありますが、これも役に立ちます。
依存性注入は、基本的に、オブジェクト自体を構築するのではなく、オブジェクトが必要とするオブジェクト(その依存関係)を提供することです。依存関係をモックアップしたりスタブアウトしたりすることができるため、テストに非常に便利な手法です。
依存関係は、さまざまな方法(コンストラクター注入やセッター注入など)によってオブジェクトに注入することができます。そのためには、特殊な依存性注入フレームワーク(Springなど)を使用することもできますが、それらは必須ではありません。これらのフレームワークに依存性注入をする必要はありません。オブジェクト(依存関係)を明示的にインスタンス化して渡すことは、フレームワークによるインジェクションと同じくらい良いインジェクションです。
この面白い例を 疎結合 :の観点から見つけました
どのようなアプリケーションでも、互いに連携して有用なものを実行するための多数のオブジェクトで構成されています。伝統的に、各オブジェクトは、それが協働する依存オブジェクト(依存関係)へのそれ自身の参照を取得する責任がある。これは密結合クラスとテストが難しいコードにつながります。
たとえば、Car
オブジェクトを考えます。
Car
は実行する車輪、エンジン、燃料、バッテリーなどに依存します。伝統的に私達はCar
オブジェクトの定義と共にそのような依存オブジェクトのブランドを定義します。
依存性注入なし(DI):
class Car{
private Wheel wh = new NepaliRubberWheel();
private Battery bt = new ExcideBattery();
//The rest
}
ここでは、Car
object が依存オブジェクトの作成を担当しています。
最初のNepaliRubberWheel()
パンクチャの後に依存オブジェクトの型を変更したい場合(Wheel
など)新しい依存関係をChineseRubberWheel()
と指定してCarオブジェクトを再作成する必要がありますが、Car
製造元のみが可能です。
それではDependency Injection
は何のために私たちをしていますか?
依存性注入を使用すると、コンパイル時(自動車製造時)ではなく実行時にオブジェクトに依存関係が与えられます。。これで、必要に応じてWheel
を変更できるようになりました。ここでは、実行時にdependency
wheel
)をCar
に挿入できます。
依存性注入を使用した後:
ここでは、実行時に 注入 the 依存関係 (Wheel and Battery)になります。したがって、用語は依存性注入です。
class Car{
private Wheel wh = [Inject an Instance of Wheel (dependency of car) at runtime]
private Battery bt = [Inject an Instance of Battery (dependency of car) at runtime]
Car(Wheel wh,Battery bt) {
this.wh = wh;
this.bt = bt;
}
//Or we can have setters
void setWheel(Wheel wh) {
this.wh = wh;
}
}
出典:_(依存性注入を理解する
依存性注入は、オブジェクトを内部的に構築するのではなく、他のコードからオブジェクトのインスタンスを受け取る方法でオブジェクトを設計する方法です。これは、オブジェクトが必要とするインターフェースを実装しているすべてのオブジェクトを、コードを変更せずに代用できることを意味します。これにより、テストが簡単になり、デカップリングが向上します。
たとえば、次の句を検討してください。
public class PersonService {
public void addManager( Person employee, Person newManager ) { ... }
public void removeManager( Person employee, Person oldManager ) { ... }
public Group getGroupByManager( Person manager ) { ... }
}
public class GroupMembershipService() {
public void addPersonToGroup( Person person, Group group ) { ... }
public void removePersonFromGroup( Person person, Group group ) { ... }
}
この例では、PersonService::addManager
およびPersonService::removeManager
の実装は、その作業を行うためにGroupMembershipService
name__のインスタンスを必要とします。 Dependency Injectionがない場合、これを行うための従来の方法は、GroupMembershipService
name__のコンストラクターに新しいPersonService
name__をインスタンス化し、そのインスタンス属性を両方の関数で使用することです。ただし、GroupMembershipService
name__のコンストラクタに必要なものが複数ある場合、またはさらに悪いことに、GroupMembershipService
name__で呼び出す必要がある初期化「セッター」がある場合、コードはかなり速く成長し、PersonService
name__はGroupMembershipService
name__だけではなくなりますGroupMembershipService
name__が依存する他のすべてのものも。さらに、GroupMembershipService
name__へのリンケージはPersonService
name__にハードコードされているため、テスト目的でGroupMembershipService
name__を「ダミーアップ」したり、アプリケーションのさまざまな部分でストラテジーパターンを使用することはできません。
依存性注入では、GroupMembershipService
name__内でPersonService
name__をインスタンス化する代わりに、それをPersonService
name__コンストラクターに渡すか、またはProperty(getterおよびsetter)を追加してそのローカルインスタンスを設定します。これは、あなたのPersonService
name__がGroupMembershipService
name__を作成する方法についてもはや心配する必要がなく、それが与えられたものを単に受け入れ、そしてそれらと共に働くことを意味します。これはまた、GroupMembershipService
name__のサブクラスである、またはGroupMembershipService
name__インターフェースを実装するものはすべてPersonService
name__に「注入」でき、PersonService
name__はその変更について知る必要がないことを意味します。
受け入れられた答えは良いものです - しかし、私はDIがコードの中でハードコードされた定数を避ける古典的なものと非常によく似ていることをこれに加えたいと思います。
データベース名のような定数を使うときは、それをコードの内側からある設定ファイルにすばやく移動し、その値を含む変数を必要な場所に渡します。その理由は、これらの定数は通常、他のコードよりも頻繁に変更されるためです。たとえば、テストデータベースでコードをテストしたい場合です。
オブジェクト指向プログラミングの世界では、DIはこれに似ています。定数リテラルの代わりにそこにある値はオブジェクト全体ですが、それらを作成するコードをクラスコードから移動する理由は似ています - オブジェクトはそれらを使用するコードよりも頻繁に変更されます。そのような変更が必要とされる1つの重要なケースはテストです。
Car および Engine クラスを使用した簡単な例を試してみましょう。少なくとも今のところは、どの車にもエンジンが必要です。そのため、以下では依存性注入なしでコードがどのように見えるかを説明します。
public class Car
{
public Car()
{
GasEngine engine = new GasEngine();
engine.Start();
}
}
public class GasEngine
{
public void Start()
{
Console.WriteLine("I use gas as my fuel!");
}
}
Carクラスをインスタンス化するために、次のコードを使います。
Car car = new Car();
このコードの問題は、GasEngineと密接に関連しており、それをElectricityEngineに変更する場合は、Carクラスを書き直す必要があります。そして、アプリケーションが大きくなればなるほど、新しいタイプのエンジンを追加して使用しなければならなくなる問題や頭痛の種が増えます。
言い換えれば、このアプローチでは、私たちの高レベルのCarクラスは、SOLIDのDependency Inversion Principle(DIP)に違反する低レベルのGasEngineクラスに依存しています。 DIPは、具体的なクラスではなく抽象化に頼るべきだと示唆しています。これを満たすために、IEngineインターフェースを導入し、以下のようにコードを書き換えます。
public interface IEngine
{
void Start();
}
public class GasEngine : IEngine
{
public void Start()
{
Console.WriteLine("I use gas as my fuel!");
}
}
public class ElectricityEngine : IEngine
{
public void Start()
{
Console.WriteLine("I am electrocar");
}
}
public class Car
{
private readonly IEngine _engine;
public Car(IEngine engine)
{
_engine = engine;
}
public void Run()
{
_engine.Start();
}
}
現在、私たちのCarクラスは、エンジンの特定の実装ではなく、IEngineインターフェースのみに依存しています。 今、唯一のトリックは、どのようにしてCarのインスタンスを作成し、それにGasEngineまたはElectricityEngineのような実際の具体的なEngineクラスを与えるかです。 依存性注入 が入ってくるところです。
Car gasCar = new Car(new GasEngine());
gasCar.Run();
Car electroCar = new Car(new ElectricityEngine());
electroCar.Run();
ここでは基本的にCarコンストラクターに依存関係(Engineインスタンス)を注入(渡し)します。それで今我々のクラスはオブジェクトとそれらの依存関係の間の疎結合を持っています、そして我々はCarクラスを変えることなしに新しいタイプのエンジンを簡単に加えることができます。
Dependency Injection の主な利点 - /クラスはより緩やかに結合されています、なぜならそれらはハードコーディングされた依存関係を持っていないからです。これは、先に述べた依存性反転原理に従います。特定の実装を参照する代わりに、クラスはクラスが構築されたときにそれらに提供される抽象化(通常は interfaces )を要求します。
つまり、 依存性注入 は、オブジェクトとそれらの依存関係の間の疎結合を達成するための単なるテクニックです。クラスが必要とする依存関係を直接インスタンス化するのではなくそのアクションを実行すると、依存関係はコンストラクター注入によってクラスに(ほとんどの場合)提供されます。
また、多くの依存関係がある場合は、すべての依存関係に対してどのインターフェイスをどの具体的な実装にマッピングする必要があるかを示すことができるInversion of Control(IoC)コンテナを使用することをお勧めします。私たちの目的たとえば、IoCコンテナのマッピングで、 IEngine 依存関係を GasEngine クラスにマッピングし、IoCコンテナに Car クラスのインスタンスを要求するように指定できます。それは自動的に Gas 依存関係を渡された Car クラスを構築するでしょう。
更新日: /最近Julie LermanからEF Coreについてのコースを見て、またDIについての彼女の短い定義が好きだった。
依存性注入は、それらのクラスがそれらのオブジェクトに対して責任を負うことを強いることなく、アプリケーションがそれらを必要とするクラスにその場でオブジェクトを注入することを可能にするパターンです。それはあなたのコードがより疎結合されることを可能にし、Entity Framework Coreはこれと同じサービスシステムにプラグインします。
あなたが釣りに行きたいと想像してみましょう:
依存性注入なしで、あなたは自分自身ですべての世話をする必要があります。ボートを探す、釣り竿を買う、餌を探すなどが必要です。もちろん可能ですが、それはあなたに多くの責任を負わせます。ソフトウェアの用語では、それはあなたがこれらすべてのもののためにルックアップを実行しなければならないことを意味します。
依存性注入では、他の誰かがすべての準備を引き受け、必要な機器を利用できるようにします。あなたはボート、釣り竿、そして餌を受け取ります(「注入されます」)。
これ は Dependency Injection および Dependency Injection Container についての最も簡単な説明です。
依存性注入 と 依存性注入コンテナ は別物です。
あなたは依存性注入をするためにコンテナを必要としません。しかし容器はあなたを助けることができます。
「依存性注入」とは、単にパラメータ化されたコンストラクタやパブリックセッタを使うという意味ではありませんか?
James Shoreの記事は比較のために次の例を示しています 。
依存性注入のないコンストラクタ
public class Example {
private DatabaseThingie myDatabase;
public Example() {
myDatabase = new DatabaseThingie();
}
public void doStuff() {
...
myDatabase.getData();
...
}
}
依存性注入のあるコンストラクタ
public class Example {
private DatabaseThingie myDatabase;
public Example(DatabaseThingie useThisDatabaseInstead) {
myDatabase = useThisDatabaseInstead;
}
public void doStuff() {
...
myDatabase.getData();
...
}
}
依存性注入(DI)とは何ですか?
他の人が言ったように、Dependency Injection(DI)は、関心のあるクラス(消費者クラス)が依存する他のオブジェクトインスタンスの直接作成と寿命の管理の責任を取り除きます(- MLセンス )。代わりに、これらのインスタンスは、通常、コンストラクターパラメーターとして、またはプロパティセッターを介して、コンシューマクラスに渡されます(コンシューマクラスへのインスタンス化とパスの依存関係オブジェクトの管理は、通常Inversion of Control(IoC)コンテナですが、それは別のトピックです)。
DI、DIPおよびSOLID
具体的には、Robert C Martinの Object Oriented Designの固体原理 のパラダイムでは、DI
は Dependency Inversion Principle(DIP) の可能な実装の1つです。 DIPはD
マントラのSOLID
です -他のDIP実装には、Service LocatorおよびPluginパターンが含まれます。
DIPの目的は、クラス間の緊密で具体的な依存関係を分離することです。代わりに、使用する言語とアプローチに応じて、interface
、abstract class
、またはpure virtual class
を介して達成できる抽象化によって結合を緩めます。
DIPがない場合、私たちのコード(私はこの「消費クラス」と呼んでいます)は具体的な依存関係に直接結合されており、この依存関係のインスタンスを取得および管理する方法を知る責任、つまり概念的にもしばしば負担になります:
"I need to create/use a Foo and invoke method `GetBar()`"
一方、DIPの適用後、要件は緩和され、Foo
依存関係の有効期間を取得および管理する懸念はなくなりました。
"I need to invoke something which offers `GetBar()`"
DIP(およびDI)を使用する理由
この方法でクラス間の依存関係を分離すると、抽象化の前提条件も満たす他の実装とのこれらの依存関係クラスのeasy substitutionが可能になります(たとえば、同じインターフェイスの別の実装で依存関係を切り替えることができます)。さらに、他の人が言及したように、おそらくthe DIPを介してクラスを分離する最も一般的な理由は、これらの同じ依存関係をスタブおよび/またはモックできるため、消費クラスを分離してテストできるようにするためです。
DIの結果の1つは、依存関係オブジェクトが(コンストラクターまたはセッターインジェクションを介して)消費クラスに渡されるため、依存オブジェクトインスタンスの寿命管理が消費クラスによって制御されなくなることです。
これはさまざまな方法で表示できます。
Create
を介してインスタンスを取得し、これらのインスタンスを完了すると破棄できます。DIを使用する場合?
MyDepClass
はスレッドセーフです-シングルトンにしてすべてのコンシューマに同じインスタンスを注入するとどうなりますか?)例
簡単なC#実装を次に示します。以下の消費クラスがある場合:
public class MyLogger
{
public void LogRecord(string somethingToLog)
{
Console.WriteLine("{0:HH:mm:ss} - {1}", DateTime.Now, somethingToLog);
}
}
一見無害に見えますが、他の2つのクラスSystem.DateTime
とSystem.Console
に2つのstatic
依存関係があり、これらはロギング出力オプションを制限するだけでなく(誰も見ていない場合、コンソールへのロギングは価値がありません)、さらに悪いことに、与えられたテストを自動的に行うことは困難です非決定的なシステムクロックへの依存。
ただし、タイムスタンプの依存関係を依存関係として抽象化し、DIP
を単純なインターフェイスにのみ結合することにより、このクラスにMyLogger
を適用できます。
public interface IClock
{
DateTime Now { get; }
}
また、Console
への依存関係を、TextWriter
などの抽象化に緩めることもできます。依存性注入は、通常、constructor
注入(抽象化を消費クラスのコンストラクターへのパラメーターとして依存性に渡す)またはSetter Injection
(setXyz()
setterまたは{set;}
を定義した.Netプロパティを介して依存関係を渡す)として実装されます。コンストラクターインジェクションが推奨されます。これにより、構築後にクラスが正しい状態になり、内部依存関係フィールドをreadonly
(C#)またはfinal
(Java)としてマークできるようになるためです。したがって、上記の例でコンストラクター注入を使用すると、次のようになります。
public class MyLogger : ILogger // Others will depend on our logger.
{
private readonly TextWriter _output;
private readonly IClock _clock;
// Dependencies are injected through the constructor
public MyLogger(TextWriter stream, IClock clock)
{
_output = stream;
_clock = clock;
}
public void LogRecord(string somethingToLog)
{
// We can now use our dependencies through the abstraction
// and without knowledge of the lifespans of the dependencies
_output.Write("{0:yyyy-MM-dd HH:mm:ss} - {1}", _clock.Now, somethingToLog);
}
}
(具体的なClock
を提供する必要があります。もちろん、これはDateTime.Now
に戻すことができ、2つの依存関係はコンストラクターインジェクションを介してIoCコンテナーによって提供する必要があります)
自動化された単体テストを構築できます。これにより、ロガーが正しく動作していることを明確に証明できます。依存関係-時間を制御できるようになり、書き込まれた出力を確認できます。
[Test]
public void LoggingMustRecordAllInformationAndStampTheTime()
{
// Arrange
var mockClock = new Mock<IClock>();
mockClock.Setup(c => c.Now).Returns(new DateTime(2015, 4, 11, 12, 31, 45));
var fakeConsole = new StringWriter();
// Act
new MyLogger(fakeConsole, mockClock.Object)
.LogRecord("Foo");
// Assert
Assert.AreEqual("2015-04-11 12:31:45 - Foo", fakeConsole.ToString());
}
次のステップ
依存関係の注入は、常に Inversion of Control container(IoC) に関連付けられ、具体的な依存関係インスタンスを注入(提供)し、寿命インスタンスを管理します。構成/ブートストラップ処理中に、IoC
コンテナにより、以下を定義できます。
IBar
を要求するたびに、ConcreteBar
インスタンスを返す")IDisposable
などのプロトコルを認識しており、構成されたライフスパン管理に従ってDisposing
依存関係の責任を引き受けます。通常、IoCコンテナーの構成/ブートストラップが完了すると、バックグラウンドでシームレスに動作し、コーダーは依存関係を気にすることなく手元のコードに集中できます。
DIフレンドリーなコードの鍵は、クラスの静的な結合を避け、依存関係の作成にnew()を使用しないことです
上記の例のように、依存関係の分離には設計作業が必要であり、開発者にとっては、new
ing依存関係の習慣を直接破り、代わりに依存関係を管理するためにコンテナを信頼するために必要なパラダイムシフトがあります。
しかし、特に関心のあるクラスを徹底的にテストできるという点で、多くの利点があります。
注:POCO/POJO /シリアル化DTO /エンティティグラフ/匿名JSONプロジェクションなどの作成/マッピング/プロジェクション(new ..()
経由) -すなわち、「データのみ」のクラスまたはレコード-使用またはメソッドから返されるnotは、依存関係(UMLの意味で)と見なされ、DIの対象ではありません。 new
を使用してこれらを投影するのは問題ありません。
依存性注入の概念を理解しやすくするため。電球を切り替える(オン/オフ)ためのスイッチボタンの例を見てみましょう。
スイッチはどの電球に接続されているかを事前に知っておく必要があります(ハードコードされた依存関係)。そう、
Switch - > PermanentBulb //スイッチは恒久的な電球に直接接続されているため、テストは簡単にできません
Switch(){
PermanentBulb = new Bulb();
PermanentBulb.Toggle();
}
スイッチは、電球が私に渡された場合は、オン/オフを切り替える必要があることを知っているだけです。そう、
スイッチ - >電球1 OR電球2 OR NightBulb(依存関係の注入)
Switch(AnyBulb){ //pass it whichever bulb you like
AnyBulb.Toggle();
}
James スイッチと電球の例の変更:
public class SwitchTest {
TestToggleBulb() {
MockBulb mockbulb = new MockBulb();
// MockBulb is a subclass of Bulb, so we can
// "inject" it here:
Switch switch = new Switch(mockBulb);
switch.ToggleBulb();
mockBulb.AssertToggleWasCalled();
}
}
public class Switch {
private Bulb myBulb;
public Switch() {
myBulb = new Bulb();
}
public Switch(Bulb useThisBulbInstead) {
myBulb = useThisBulbInstead;
}
public void ToggleBulb() {
...
myBulb.Toggle();
...
}
}`
依存性注入(DI)のポイントは、アプリケーションのソースコードを clean および stable に保つことです。
実際には、すべてのデザインパターンが懸念を切り離し、将来の変更が最小限のファイルに影響を与えるようにします。
DIの特定のドメインは、依存関係の設定と初期化の委任です。
ときどきJavaの外部で作業する場合は、source
name__が多くのスクリプト言語(シェル、Tclなど、またはこの目的のために誤用されたPythonのimport
name__)でよく使用される方法を思い出してください。
単純なdependent.sh
スクリプトを考えてください。
#!/bin/sh
# Dependent
touch "one.txt" "two.txt"
archive_files "one.txt" "two.txt"
このスクリプトは依存しています。単独では正常に実行されません(archive_files
は定義されていません)。
archive_files
実装スクリプトでarchive_files_Zip.sh
を定義します(この場合はZip
name__を使用します)。
#!/bin/sh
# Dependency
function archive_files {
Zip files.Zip "$@"
}
source
name実装スクリプトを直接依存スクリプトに含める代わりに、両方の「コンポーネント」をラップするinjector.sh
「コンテナ」を使用します。
#!/bin/sh
# Injector
source ./archive_files_Zip.sh
source ./dependent.sh
archive_files
dependency は 注入 を dependent スクリプトに追加したところです。
tar
name__またはxz
name__を使ってarchive_files
を実装する依存性を注入したかもしれません。
dependent.sh
スクリプトが依存関係を直接使用していた場合、そのアプローチは 依存関係検索 となります(これは 依存関係注入 とは反対です)。
#!/bin/sh
# Dependent
# dependency look-up
source ./archive_files_Zip.sh
touch "one.txt" "two.txt"
archive_files "one.txt" "two.txt"
今問題は、依存する「コンポーネント」が初期化自体を実行しなければならないということです。
"component"のソースコードは clean でも stable でもありません。依存関係の初期化を変更するたびに "components"のソースコードファイルの新しいリリースが必要になるためです。
DIは、Javaフレームワークほど広くは強調されず普及していません。
しかし、それは懸念を分ける一般的なアプローチです。
dependency lookup のみで設定を使用することは、依存ごとにサポートされる依存の種類の数(新しいデータベースの種類など)と同様に、依存関係ごとに設定パラメータの数が変わる可能性があるため役に立ちません。
上記の答えはすべて良いです、私の目的は、プログラミングの知識がない人でも概念を理解できるように、簡単な方法で概念を説明することです
依存性注入は、複雑なシステムをより簡単に作成するのに役立つ設計パターンの1つです。
私たちは日々の生活の中でこのパターンの多種多様なアプリケーションを見ることができます。いくつかの例は、テープレコーダー、VCD、CDドライブなどです。
上記の画像は、20世紀半ばのリールツーリールポータブルテープレコーダーの画像です。 ソース 。
テープレコーダーマシンの主な目的は、サウンドを録音または再生することです。
システムの設計中に、サウンドまたは音楽を録音または再生するにはリールが必要です。このシステムを設計するには2つの可能性があります
最初のものを使用する場合は、マシンを開いてリールを変更する必要があります。 2番目のリール、つまりリール用のフックを選択した場合、リールを変更することで音楽を再生できるという追加の利点が得られます。また、リール内の何でも演奏することだけに機能を減らします。
賢明な依存関係注入と同様に、依存関係を外部化してコンポーネントの特定の機能のみに焦点を当て、独立したコンポーネントを結合して複雑なシステムを形成できるようにするプロセスです。
依存性注入を使用して達成した主な利点。
現在、これらの概念は、プログラミングの世界でよく知られているフレームワークの基礎を形成しています。 Spring Angularなどは、この概念の上に構築された有名なソフトウェアフレームワークです。
依存性注入は、コンパイル時に他のオブジェクトが依存するオブジェクトのインスタンスを作成するために使用されるパターンです。どのクラスを使用してその機能を提供するか、またはオブジェクトにプロパティを注入する方法を依存性注入と呼びます。
依存性注入の例
以前は、このようなコードを書いていました
Public MyClass{
DependentClass dependentObject
/*
At somewhere in our code we need to instantiate
the object with new operator inorder to use it or perform some method.
*/
dependentObject= new DependentClass();
dependentObject.someMethod();
}
依存性注入では、依存性インジェクターがインスタンス化を解除します
Public MyClass{
/* Dependency injector will instantiate object*/
DependentClass dependentObject
/*
At somewhere in our code we perform some method.
The process of instantiation will be handled by the dependency injector
*/
dependentObject.someMethod();
}
また読むことができます
依存性注入(DI)は、互いに依存しているオブジェクトを分離することを意味します。オブジェクトAがオブジェクトBに依存しているとすると、アイデアはこれらのオブジェクトを互いに切り離すことです。コンパイル時にもかかわらず、実行時にオブジェクトへの依存関係を共有するのではなく、新しいキーワードを使用してオブジェクトをハードコーディングする必要はありません。
コンフィグレーションファイルでBeanの依存関係を定義するのではなく、newキーワードを使用してオブジェクトをハードコードする必要はありません。春コンテナはすべてをフックする責任があります。
IOCは一般的な概念であり、さまざまな方法で表現することができ、依存性注入はIOCの1つの具体例です。
コンストラクタベースのDIは、コンテナが、それぞれ他のクラスへの依存関係を表す多数の引数を使用してクラスコンストラクタを呼び出すときに実行されます。
public class Triangle {
private String type;
public String getType(){
return type;
}
public Triangle(String type){ //constructor injection
this.type=type;
}
}
<bean id=triangle" class ="com.test.dependencyInjection.Triangle">
<constructor-arg value="20"/>
</bean>
セッターベースのDIは、引数のないコンストラクターまたは引数のない静的ファクトリーメソッドを呼び出してBeanをインスタンス化した後に、コンテナーがBeanのセッターメソッドを呼び出すことによって実現されます。
public class Triangle{
private String type;
public String getType(){
return type;
}
public void setType(String type){ //setter injection
this.type = type;
}
}
<!-- setter injection -->
<bean id="triangle" class="com.test.dependencyInjection.Triangle">
<property name="type" value="equivialteral"/>
注:必須の依存関係にはコンストラクター引数を使用し、オプションの依存関係にはセッターを使用することをお勧めします。 setterに@Requiredアノテーションよりもアノテーションベースを使用する場合は、必要な依存関係としてsetterを作成するために使用できます。
私が考えることができる最もよく似たものは外科医と手術室の彼の助手であり、そこでは外科医が主な人物であり、そして外科医が必要なときに様々な外科用部品を提供する彼が一番やること(手術)。アシスタントがなければ、外科医は自分が必要とするたびに自分で部品を手に入れなければなりません。
略してDIは、依存コンポーネントをそれに提供することによってそれをフェッチするためのコンポーネントに対する共通の追加の責任(負担)を取り除くための技法です。
DIは、surgeon who can concentrate on surgery
のように、単一責任(SR)の原則に近づきます。
DIを使用する場合:ほとんどすべてのプロダクションプロジェクト(小規模/大規模)、特に絶え間なく変化するビジネス環境でDIを使用することをお勧めします。
理由:変更をすばやくテストして市場に投入できるように、コードを簡単にテスト可能、モック可能などにしたいのです。あなたがより多くのコントロールを持っているコードベースへの旅行であなたをサポートするための素晴らしい無料のツール/フレームワークがたくさんあるのに、なぜあなたはそうしないのですか。
つまり、オブジェクトは、その役割を果たすのに必要なだけの依存関係を持ち、依存関係は少なくなるはずです。さらに、可能であれば、オブジェクトの依存関係は「具象」オブジェクトではなく、インタフェースに依存する必要があります。疎結合は、再利用性を高め、保守性を高め、高価なサービスの代わりに「モック」オブジェクトを簡単に提供できるようにします。
「依存性注入」(DI)は「制御の反転」(IoC)とも呼ばれ、この疎結合を促進するための手法として使用できます。
DIを実装するには、主に2つの方法があります。
これは、オブジェクトの依存関係をコンストラクタに渡す手法です。
コンストラクタは、具象オブジェクトではなくインタフェースを受け入れます。また、orderDaoパラメータがnullの場合は例外がスローされます。これは、有効な依存関係を受け取ることの重要性を強調しています。私の考えでは、コンストラクタインジェクションはオブジェクトにその依存関係を与えるための好ましいメカニズムです。適切な実行のためにどの依存性が“ Person”オブジェクトに与えられる必要があるかは、オブジェクトを呼び出す間、開発者にとって明らかである。
しかし、次の例を考えてみましょう。依存関係のない10個のメソッドを持つクラスがあり、IDAOに依存する新しいメソッドを追加しているとします。あなたはConstructor Injectionを使うようにコンストラクタを変更することができます、しかしこれはあなたに至る所ですべてのコンストラクタ呼び出しへの変更を強制するかもしれません。あるいは、依存関係を取る新しいコンストラクターを追加するだけで、開発者は、あるコンストラクターを他のコンストラクターに対してどのように使用するかを簡単に知ることができます。最後に、依存関係を作成するのに非常にコストがかかる場合、それがめったに使用されない場合になぜそれを作成してコンストラクタに渡す必要があるのでしょうか。 「セッター注入」は、このような状況で使用できるもう1つのDI技法です。
Setter Injectionは、依存関係がコンストラクタに渡されることを強制しません。代わりに、依存関係は、必要としているオブジェクトによって公開されるパブリックプロパティに設定されます。前述のように、これを行う主な動機は次のとおりです。
これは上記のコードがどのように見えるかの例です。
public class Person {
public Person() {}
public IDAO Address {
set { addressdao = value; }
get {
if (addressdao == null)
throw new MemberAccessException("addressdao" +
" has not been initialized");
return addressdao;
}
}
public Address GetAddress() {
// ... code that uses the addressdao object
// to fetch address details from the datasource ...
}
// Should not be called directly;
// use the public property instead
private IDAO addressdao;
例として、2つのクラスClient
とService
があります。 Client
はService
を使用します
public class Service {
public void doSomeThingInService() {
// ...
}
}
方法1)
public class Client {
public void doSomeThingInClient() {
Service service = new Service();
service.doSomeThingInService();
}
}
方法2)
public class Client {
Service service = new Service();
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
方法3)
public class Client {
Service service;
public Client() {
service = new Service();
}
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
1)2)3) を使う
Client client = new Client();
client.doSomeThingInService();
利点
デメリット
Client
クラスService
コンストラクタを変更するとき、すべての場所でコードを変更する必要がありますcreate Service
object方法1) コンストラクタ注入
public class Client {
Service service;
Client(Service service) {
this.service = service;
}
// Example Client has 2 dependency
// Client(Service service, IDatabas database) {
// this.service = service;
// this.database = database;
// }
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
使用中
Client client = new Client(new Service());
// Client client = new Client(new Service(), new SqliteDatabase());
client.doSomeThingInClient();
方法2) セッター注入
public class Client {
Service service;
public void setService(Service service) {
this.service = service;
}
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
使用中
Client client = new Client();
client.setService(new Service());
client.doSomeThingInClient();
方法3) インターフェース注入
チェック https://en.wikipedia.org/wiki/Dependency_injection
===
さて、このコードはすでにDependency Injection
に従っており、テストClient
クラスのほうが簡単です。
しかし、私たちはまだnew Service()
を何度も使っていて、Service
コンストラクタを変更するのは良くありません。それを防ぐために、DIインジェクタを使うことができます
1)簡単なマニュアルInjector
public class Injector {
public static Service provideService(){
return new Service();
}
public static IDatabase provideDatatBase(){
return new SqliteDatabase();
}
public static ObjectA provideObjectA(){
return new ObjectA(provideService(...));
}
}
を使用
Service service = Injector.provideService();
2)使用ライブラリ:Android用 dagger2
利点
Service
を変えるとき、あなたはそれをInjectorクラスで変える必要があるだけですConstructor Injection
を使った場合、Client
のコンストラクタを見ると、Client
クラスの依存関係がいくつあるかがわかります。デメリット
Constructor Injection
を使用する場合、Service
オブジェクトはClient
が作成されたときに作成されます。ときにはClient
を使用せずにService
クラスで関数を使用するので、作成されたService
は無駄になります。https://en.wikipedia.org/wiki/Dependency_injection
依存関係は使用できるオブジェクトです(
Service
)
注入とは、依存関係(Service
)をそれを使用する依存オブジェクト(Client
)に渡すことです。
みんながDIのために書いたので、私はいくつかの質問をさせてください..
これは、@ Adam Nが投稿した回答に基づいています。
なぜPersonServiceはGroupMembershipServiceについて心配する必要がなくなったのですか? GroupMembershipには、それが依存する複数のもの(オブジェクト/プロパティ)があることを説明しました。 PServiceにGMServiceが必要な場合は、それをプロパティとして使用します。あなたがそれを注射したかどうかにかかわらず、あなたはそれを模擬することができます。私がそれを注入したいのは、GMServiceがより具体的な子クラスを持っている場合だけです。これは実行時までわからないでしょう。それならサブクラスを注入したいのです。または、シングルトンまたはプロトタイプとしてそれを使用したい場合は。正直に言うと、設定ファイルには、コンパイル時にどのサブクラスの型(インタフェース)を挿入するかについて、ハードコーディングされたものがすべて含まれています。
_編集_
DIは依存関係の方向性を判断し、グルーコードを書く必要性を排除することによって結束力を高めます。
偽です。依存関係の方向はXML形式であるかアノテーションとしてあります。あなたの依存関係はXMLコードとアノテーションとして書かれています。 XMLと注釈はソースコードです。
DIは、すべてのコンポーネントをモジュール式(交換可能)にすることでカップリングを減らし、互いに明確に定義されたインターフェースを持ちます。
偽です。あなたは、インターフェイスに基づいてモジュラーコードを構築するためにDIフレームワークを必要としません。
置き換え可能について:非常に単純な.propertiesアーカイブとClass.forNameを使って、クラスを変更できるように定義することができます。あなたのコードのクラスを変更することができるのであれば、Javaはあなたのためではありません。スクリプト言語を使用してください。ところで、注釈は再コンパイルしないと変更できません。
私の意見では、DIの枠組みにはボイラープレートの削減という唯一の理由があります。優れたファクトリシステムを使用すると、好みのDIフレームワークと同じ、より制御された、予測可能なものを実行できます。DIフレームワークはコード削減を約束します(XMLと注釈もソースコードです)。問題は、このボイラープレートの削減が非常に単純な場合(クラスごとに1つのインスタンスなど)では実際には発生することです。
依存性注入 は、コードの一部(クラスなど)が依存関係(コードの他の部分、他のクラスなどに依存する)にアクセスするための方法(実際には any-way )を意味します。ハードコードされていなくてもモジュール方式で(必要に応じて自由に変更したり上書きしたり、別の時点で読み込むこともできます)
(そしてps、そう、それはかなり単純な概念のために過度に宣伝された25 $の名前になった)、私の.25
セント
私はすでに多くの答えがあることを知っています、しかし私はこれが非常に役に立ちました: http://tutorials.jenkov.com/dependency-injection/index.html
依存関係なし
public class MyDao {
protected DataSource dataSource =
new DataSourceImpl("driver", "url", "user", "password");
//data access methods...
public Person readPerson(int primaryKey) {...}
}
依存:
public class MyDao {
protected DataSource dataSource = null;
public MyDao(String driver, String url, String user, String
password){
this.dataSource = new DataSourceImpl(driver, url, user, password);
}
//data access methods...
public Person readPerson(int primaryKey)
{...}
}
DataSourceImpl
のインスタンス化がどのようにコンストラクターに移動されたかに注目してください。コンストラクタはDataSourceImpl
が必要とする4つの値である4つのパラメータを取ります。 MyDao
クラスは依然としてこれら4つの値に依存していますが、もはやこれらの依存関係自体を満たすことはできません。それらはMyDao
インスタンスを作成するどんなクラスによっても提供されます。
人気のある答えは役に立たない、なぜならそれらは有用ではない方法で依存性注入を定義するからである。 「依存性」とは、オブジェクトXが必要とする既存の他のオブジェクトを意味することに同意しましょう。しかし、私たちが言うときに「依存性注入」をしているとは言いません。
$foo = Foo->new($bar);
コンストラクタに渡すパラメータを呼び出すだけです。コンストラクタが発明されて以来、私たちはこれを定期的に行ってきました。
「依存性注入」は一種の「制御の逆転」と見なされます。つまり、呼び出し側から何らかのロジックが取り出されるということです。呼び出し側がパラメータを渡す場合はそうではありません。したがって、それがDIの場合、DIは制御の逆転を意味するものではありません。
DIは、呼び出し元と依存関係を管理するコンストラクターの間に中間レベルがあることを意味します。 Makefileは依存性注入の簡単な例です。 「呼び出し元」はコマンドラインで「make bar」と入力する人であり、「コンストラクタ」はコンパイラです。 Makefileはbarがfooに依存することを指定し、そして
gcc -c foo.cpp; gcc -c bar.cpp
する前に
gcc foo.o bar.o -o bar
"make bar"とタイプした人は、barがfooに依存していることを知る必要はありません。依存関係は "make bar"とgccの間に挿入されました。
中間レベルの主な目的は、依存関係をコンストラクターに渡すだけでなく、すべての依存関係を 1か所 - にリストし、それらをコーダーから隠すことです(コーダーにそれらを提供させないため) 。
通常、中間レベルは構築されたオブジェクトのファクトリを提供します。これは、要求された各オブジェクトタイプが満たす必要がある役割を提供する必要があります。それは、建設の詳細を隠す中間レベルを持つことによって、すでに工場によって課された抽象化のペナルティを被っているので、工場を使うこともできるからです。
依存性注入は、一般に「依存性難読化」要件と呼ばれるものに対する考えられる解決策の1つです。依存関係の難読化は、それを必要とするクラスに依存関係を提供するプロセスから「明白な」性質を取り去り、そのため、何らかの方法で、そのクラスへの依存関係の提供を難読化する方法です。これは必ずしも悪いことではありません。実際、依存関係をクラスに提供する方法を難読化することにより、クラス外の何かが依存関係を作成する責任があります。これは、さまざまなシナリオで、変更を加えずに依存関係の異なる実装をクラスに提供できることを意味しますクラスに。これは、プロダクションモードとテストモードを切り替えるのに最適です(たとえば、「モック」サービスの依存関係を使用する)。
残念なことに、一部の人々は、依存関係の難読化を行うために特別なフレームワークが必要であると想定していることと、特定のフレームワークを使用しないことを選択した場合、何らかの方法で「より少ない」プログラマーであるということです。多くの人が信じている非常に邪魔なもう一つの神話は、依存性注入が依存性難読化を達成する唯一の方法であるというものです。これは明らかに歴史的にも明らかに100%間違っていますが、依存関係の難読化要件に依存関係の注入に代わるものがあることを一部の人々に納得させるのは困難です。
プログラマーは何年もの間依存関係の難読化の要件を理解しており、多くの代替ソリューションが依存関係の注入が考えられる前後に進化してきました。 Factoryパターンはありますが、特定のインスタンスへの注入が不要なThreadLocalを使用する多くのオプションもあります-依存関係は、オブジェクトを(便利な静的getterメソッドを介して)利用可能にする利点があるスレッドに効果的に注入されますanyそれを必要とするクラスは、それを必要とするクラスに注釈を追加する必要なく、それを実現するために複雑なXML「接着剤」をセットアップする必要はありません。永続性(JPA/JDOなど)に依存関係が必要な場合、POJOだけで構成されるドメインモデルとビジネスモデルクラス(つまり、フレームワーク固有/アノテーションでロックされていない)を使用して、「透過的永続性」をはるかに簡単に実現できます。
『The Book from the /』、「 十分に根拠のあるJava Developer:Java 7の重要なテクニック、そしてポリグロット・プログラミング
DIはIoCの特定の形式であり、依存関係を見つけるプロセスは、現在実行中のコードを直接制御することはできません。
依存性注入(DI)は、デザインパターンの1つで、OOPの基本機能、つまりあるオブジェクトと別のオブジェクトとの関係を使用します。継承は1つのオブジェクトを継承して、より複雑で具体的な別のオブジェクトを実行しますが、関係または関連付けは、attributeを使用して1つのオブジェクトから別のオブジェクトへのポインタを作成するだけです。 DIの力は、インタフェースや隠しコードのように、他のOOPの機能と組み合わされています。単純さ.
本のインターフェース:
package com.deepam.hidden;
public interface BookInterface {
public BookInterface setHeight(int height);
public BookInterface setPages(int pages);
public int getHeight();
public int getPages();
public String toString();
}
次に私たちは多くの種類の本を持つことができます。タイプの一つはフィクションです:
package com.deepam.hidden;
public class FictionBook implements BookInterface {
int height = 0; // height in cm
int pages = 0; // number of pages
/** constructor */
public FictionBook() {
// TODO Auto-generated constructor stub
}
@Override
public FictionBook setHeight(int height) {
this.height = height;
return this;
}
@Override
public FictionBook setPages(int pages) {
this.pages = pages;
return this;
}
@Override
public int getHeight() {
// TODO Auto-generated method stub
return height;
}
@Override
public int getPages() {
// TODO Auto-generated method stub
return pages;
}
@Override
public String toString(){
return ("height: " + height + ", " + "pages: " + pages);
}
}
今購読者は本への関連付けを持つことができます:
package com.deepam.hidden;
import Java.lang.reflect.Constructor;
import Java.lang.reflect.InvocationTargetException;
public class Subscriber {
BookInterface book;
/** constructor*/
public Subscriber() {
// TODO Auto-generated constructor stub
}
// injection I
public void setBook(BookInterface book) {
this.book = book;
}
// injection II
public BookInterface setBook(String bookName) {
try {
Class<?> cl = Class.forName(bookName);
Constructor<?> constructor = cl.getConstructor(); // use it for parameters in constructor
BookInterface book = (BookInterface) constructor.newInstance();
//book = (BookInterface) Class.forName(bookName).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return book;
}
public BookInterface getBook() {
return book;
}
public static void main(String[] args) {
}
}
3つのクラスはすべてそれ自身の実装のために隠すことができます。これでDIにこのコードを使うことができます。
package com.deepam.implement;
import com.deepam.hidden.Subscriber;
import com.deepam.hidden.FictionBook;
public class CallHiddenImplBook {
public CallHiddenImplBook() {
// TODO Auto-generated constructor stub
}
public void doIt() {
Subscriber ab = new Subscriber();
// injection I
FictionBook bookI = new FictionBook();
bookI.setHeight(30); // cm
bookI.setPages(250);
ab.setBook(bookI); // inject
System.out.println("injection I " + ab.getBook().toString());
// injection II
FictionBook bookII = ((FictionBook) ab.setBook("com.deepam.hidden.FictionBook")).setHeight(5).setPages(108); // inject and set
System.out.println("injection II " + ab.getBook().toString());
}
public static void main(String[] args) {
CallHiddenImplBook kh = new CallHiddenImplBook();
kh.doIt();
}
}
依存性注入を使用する方法はたくさんあります。シングルトンなどと組み合わせることは可能ですが、それでも基本的には他のオブジェクトの中にオブジェクト型の属性を作成することによって実現される関連付けにすぎません。何度も何度も書く必要があります私たちのために常に準備され、行われています。これが、DIが制御の反転(IoC)と非常に密接に結び付いているためです。つまり、プログラムが実行中の別のモジュールに制御を渡し、コードがBeanに注入されます。 (注入可能な各オブジェクトは署名されるか、またはBeanと見なされる可能性があります。)たとえばSpringでは、これは ApplicationContext containerの作成および初期化によって行われます。コード内でコンテキストを作成し、Beanの初期化を呼び出すだけです。その瞬間に注入は自動的に行われました。
book /から - Apress.Spring.Persistence.with.Hibernate.Oct.2010
依存性注入の目的は、外部ソフトウェアコンポーネントをアプリケーションビジネスロジックから解決する作業を分離することです。コンポーネントのコードに戸惑います。これは、エラーの可能性を高め、コードを膨らませ、そして保守の複雑さを増大させるだけではありません。それはコンポーネントをより密接に結合し、リファクタリングやテストの際に依存関係を修正することを困難にします。
簡単に言うと、依存性注入(DI)は、依存性を除去したり、異なるオブジェクト間の密接な結合を排除したりする方法です。依存性注入は、各オブジェクトにまとまりのある動作を与えます。
DIはSpringのIOCプリンシパルの実装です。依存性注入プログラマを使用すると、newキーワードを使用してオブジェクトを作成する必要がありません。
オブジェクトは一度Springコンテナにロードされ、getBean(String beanName)メソッドを使ってSpringコンテナからそれらのオブジェクトを取得することで、必要に応じていつでも再利用します。
依存性注入(DI)は、依存性反転原理(DIP)プラクティスの一部であり、制御の反転(IoC)とも呼ばれます。基本的にDIPを実行する必要があるのは、1つのモノリシックシステムではなく、コードをよりモジュール化し、単体テスト可能にしたいからです。そこで、クラスから切り離して抽象化することができるコードの部分を識別し始めます。抽象化の実装はクラスの外部からインジェクトする必要があります。通常、これはコンストラクタを介して実行できます。そのため、抽象化をパラメータとして受け入れるコンストラクタを作成します。これを(コンストラクタによる)依存性注入と呼びます。 DIP、DI、およびIoCコンテナの詳細については、 こちら を参照してください。
依存性注入は、Spring Frameworkに関連する概念の中心です。任意のプロジェクトの春のフレームワークを作成するときに、極めて重要な役割を果たすことがあります。ここでは、依存性注入が重要になります。
実際に、JavaでクラスAとクラスBの2つのクラスを作成し、クラスBで使用可能な関数が何であれ、クラスAで使用するとします。そのときは依存性注入を使用できます。あるクラスのオブジェクトを別のクラスのオブジェクトにクレートすることができます。同様に、クラス全体を別のクラスにインジェクトしてアクセスできるようにすることもできます。この方法で依存性を克服することができます。
依存性の注入IS 2つのクラスを単純にグルーピングし、ATそれらを分離したままにします。
技術的な手段ではなく、主な目的に焦点を当て、Dependency Injectionが何であるかについて、少し異なる正確で短い定義を提案します( here から続く)。
依存性注入は、サービスオブジェクトの静的なステートレスグラフを作成するプロセスです。各サービスは、その依存関係によってパラメータ化されます。
アプリケーションで作成するオブジェクトは(Java、C#、その他のオブジェクト指向言語のどちらを使用する場合でも)、通常、2つのカテゴリのいずれかに分類されます。ステートレス、静的、グローバルの「サービスオブジェクト」(モジュール)、およびステートフル、動的、ローカル「データオブジェクト」.
モジュールグラフ(サービスオブジェクトのグラフ)は通常、アプリケーションの起動時に作成されます。これは、Springなどのコンテナを使用して実行できますが、オブジェクトコンストラクタにパラメータを渡すことによって手動で実行することもできます。どちらの方法にも長所と短所がありますが、アプリケーションでDIを使用するためにフレームワークを使用する必要はありません。
1つの要件は、サービスがそれらの依存関係によってパラメータ化されなければならないということです。これが何を意味するのかは、特定のシステムで採用されている言語とアプローチによって異なります。通常、これはコンストラクタパラメータの形式を取りますが、セッターを使用することもオプションです。これはまた、サービスの依存関係がサービスのユーザーから隠されていることを意味します(サービスメソッドを呼び出すとき)。
いつ使うの?モジュール間の依存関係グラフを使用してロジックを別々のモジュールにカプセル化するのに十分なほどアプリケーションのサイズが大きい場合はいつでも、コードの読みやすさと探索性が向上します。
依存性注入は、いくつかの依存関係から切り離されたコンポーネントに依存しないようにする方法であり、これは SOLID ガイドラインに従う
依存性反転の原則:「結石ではなく、抽象化に依存する必要があります。
依存性注入のより良い実装は、コンポーネントを依存性注入コンテナから切り離すことができるため、コンポジションルートデザインパターンです。
コンポジションルートに関するこの素晴らしい記事を推奨します http://blog.ploeh.dk/2011/07/28/CompositionRoot/ Mark Seemann著
この記事の重要なポイントは次のとおりです。
コンポジションルートは、モジュールが一緒に構成されているアプリケーション内の(できれば)一意の場所です。
...
アプリケーションのみがコンポジションルートを持つ必要があります。ライブラリとフレームワークはすべきではありません。
...
DIコンテナは、コンポジションルートからのみ参照される必要があります。他のすべてのモジュールには、コンテナへの参照を含めないでください。
依存性注入フレームワークであるDi-Ninjaのドキュメントは、コンポジションルートと依存性注入の原理がどのように機能するかを示す非常に良い例です。 https://github.com/di-ninja/di-ninja 私が知っているように、コンポジションルートデザインパターンを実装するjavascriptの唯一のDiCです。
Christoffer Noringから、Pablo Deelemanの本“ Learning Angular - Second Edition ":
「私たちのアプリケーションが成長し発展するにつれて、私たちの各コードエンティティは内部的に他のオブジェクトのインスタンスを必要とします。これはソフトウェアエンジニアリングの世界では依存関係としてよく知られています。インジェクタは、必要な依存関係をインスタンス化してブートストラップする責任があるため、クライアントに正常にインジェクトされた瞬間から使用できるようになります。クライアントはそれ自身の依存関係をどのようにインスタンス化するかについては何も知りませんし、それらを使用するために彼らが実装するインタフェースを知っているだけです。」
依存性注入 は " 制御の反転 "原則の実装の一種であり、これに基づいてフレームワークが構築されます。
フレームワーク GoFの "Design Pattern"に述べられているように、開発者にそれをさせるメイン制御フローロジックを実装するクラスです。このようにして、フレームワークは制御原理の逆転を実現します。
クラス階層としてではなく、テクニックとして実装する方法です。このIoCの原則は、単に依存性注入です。
_ di _ は主に、クラスインスタンスのマッピングとそのインスタンスへの型参照を外部の「エンティティ」に委譲することで構成されます。オブジェクト、静的クラス、コンポーネント、フレームワークなど。
クラスインスタンスは " dependencies "であり、参照による呼び出し元コンポーネントとクラスインスタンスの外部バインディングは " injection "です。
OOPの観点から、このテクニックをいろいろな方法で実装することができます。例えば、 コンストラクタ注入 、 セッター注入 、 インターフェース注入を参照してください。 。
参照をオブジェクトに一致させるタスクを実行するように第三者に委任することは、サービスを必要とするコンポーネントを同じサービスの実装から完全に分離したい場合に非常に役立ちます。
このように、コンポーネントを設計するときは、使用するオブジェクトやサービスの実装の変更を気にせずに、他のオブジェクトと連携するためのインタフェースを信頼しながら、アーキテクチャと特定のロジックに専念できます。完全に置き換えられます(明らかにインターフェースを尊重します)。
重要なアプリケーションは、ビジネスロジックを実行するために互いに連携する2つ以上のクラスで構成されています。伝統的に、各オブジェクトは、それが協働するオブジェクト(その依存関係)への独自の参照を取得する責任があります。 DIを適用すると、オブジェクトは作成時にシステム内の各オブジェクトを調整する外部エンティティによって依存関係が与えられます。 つまり、依存関係はオブジェクトに注入されます。
詳しくは こちらにリンクの説明を入力してください
DIは、あるオブジェクトが別のオブジェクトの存在に責任を負うことなく、実際のオブジェクトが実際に相互作用する方法です。オブジェクトは同等に扱われるべきです。それらはすべて物です。誰もクリエイターのように振る舞うべきではありません。これが、あなたが自分の物に正義を決める方法です。
簡単な例 :
あなたが医者を必要とするならば、あなたは単に行って、(存在する)医者を見つけます。あなたはあなたを助けるために最初から医者をつくることを考えていないでしょう。彼はすでに存在していて、あなたや他の物に仕えるかもしれません。彼はあなた(一つの物)が彼を必要としているかどうかに関わらず存在する権利を有します。彼の存在は全能の神であり、自然な選択ではないと決心した人。したがって、DIの利点の1つは、宇宙の存続期間中に目的のない生きている不要な冗長オブジェクトを作成しないようにすることです(つまりアプリケーション)。