依存関係注入についての特定の質問がすでに投稿されている質問がいくつかあります。たとえば、いつ使用するか、どのフレームワークがあるかなどです。しかしながら、
依存性注入とは何ですか?いつ/なぜ使用する必要があるのですか?
依存関係注入についての特定の質問がすでに投稿されている質問がいくつかあります。たとえば、いつ使用するか、どのフレームワークがあるかなどです。しかしながら、
依存性注入とは何ですか?いつ/なぜ使用する必要があるのですか?
回答:
Dependency Injectionは、他のオブジェクトまたはフレームワーク(Dependency Injector)に依存関係を渡します。
依存性注入により、テストが容易になります。注入は、コンストラクタを介して行うことができます。
SomeClass()
次のようなコンストラクタがあります:
public SomeClass() {
myObject = Factory.getObject();
}
問題:myObject
ディスクアクセスやネットワークアクセスなどの複雑なタスクが含まれる場合、でユニットテストを実行するのは困難SomeClass()
です。プログラマはモックする必要がmyObject
あり、ファクトリコールを傍受する可能性があります。
代替ソリューション:
myObject
コンストラクタに引数として渡すpublic SomeClass (MyClass myObject) {
this.myObject = myObject;
}
myObject
直接渡すことができるため、テストが容易になります。
依存性の注入なしに単体テストでコンポーネントを分離することは困難です。
2013年にこの回答を書いたとき、これはGoogle Testing Blogの大きなテーマでした。プログラマーがランタイム設計に追加の柔軟性を常に必要とするわけではないため(たとえば、サービスロケーターや同様のパターンの場合)、それは私にとって最大の利点です。多くの場合、プログラマーはテスト中にクラスを分離する必要があります。
これまでに見つけた最良の定義は、James Shoreによるものです。
「依存性注入」は、5セントのコンセプトを表す25ドルの用語です。[...]依存性注入とは、オブジェクトにインスタンス変数を与えることです。[...]。
あるMartin Fowler氏による記事も有用であろう。
依存性注入は、基本的に、オブジェクト自体を構築するのではなく、オブジェクトが必要とするオブジェクト(その依存関係)を提供します。依存関係のモックやスタブを作成できるため、テストに非常に役立つテクニックです。
依存関係は、さまざまな方法でオブジェクトに注入できます(コンストラクター注入やセッター注入など)。専用の依存関係注入フレームワーク(Springなど)を使用してそれを行うこともできますが、必須ではありません。依存性注入を行うためにこれらのフレームワークは必要ありません。オブジェクト(依存関係)を明示的にインスタンス化して渡すことは、フレームワークによる注入と同じくらい優れた注入です。
私は疎結合の観点からこの面白い例を見つけました:
どのアプリケーションも、相互に連携していくつかの便利なことを実行する多くのオブジェクトで構成されています。従来、各オブジェクトは、連携する依存オブジェクト(依存関係)への独自の参照を取得する責任があります。これは、高度に結合されたクラスとテストが難しいコードにつながります。
たとえば、Car
オブジェクトを考えてみましょう。
A Car
は、実行する車輪、エンジン、燃料、バッテリーなどに依存します。従来、このような依存オブジェクトのブランドは、Car
オブジェクトの定義とともに定義されていました。
依存性注入なし(DI):
class Car{
private Wheel wh = new NepaliRubberWheel();
private Battery bt = new ExcideBattery();
//The rest
}
ここで、Car
オブジェクトは依存オブジェクトの作成を担当します。
Wheel
最初のNepaliRubberWheel()
パンクチャ後に、依存オブジェクトのタイプを変更したい場合はどうでしょうか。新しい依存性sayを使用してCarオブジェクトを再作成する必要がありますがChineseRubberWheel()
、これCar
を行うことができるのは製造元のみです。
それでDependency Injection
私たちに何をしますか...?
依存性注入を使用する場合、オブジェクトには、コンパイル時(自動車の製造時)ではなく、実行時に依存関係が与えられます。これで、Wheel
いつでも好きなときに変更できるようになりました。ここでは、実行時にdependency
(wheel
)を注入できますCar
。
依存性注入を使用した後:
ここで、我々はされている注入依存関係、実行時に(ホイールとバッテリーを)。したがって、用語:依存性注入。
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;
}
}
出典:依存性注入について
new
Aをタイヤ?私はしません。私がしなければならないのは、それらから購入し(パラメーターを介して注入)、インストールして、ワラです!したがって、プログラミングに戻って、C#プロジェクトで既存のライブラリ/クラスを使用する必要があるとしましょう。実行/デバッグする方法は2つあります
new
それを行い、オプション2はそれをパラメーターとして渡します。正確ではないかもしれませんが、理解しやすい単純な愚かです。
依存性注入は、オブジェクトを内部で構築するのではなく、他のコードからオブジェクトのインスタンスを受け取るようにオブジェクトを設計する方法です。つまり、オブジェクトに必要なインターフェイスを実装するオブジェクトは、コードを変更せずに置き換えることができるため、テストが簡略化され、デカップリングが改善されます。
たとえば、次の例を考えてみます。
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
、その作業を行うために。Dependency Injection を使用しない場合、これを行う従来の方法はGroupMembershipService
、コンストラクタのnewをインスタンス化し、PersonService
両方の関数でそのインスタンス属性を使用することです。ただし、のコンストラクタにGroupMembershipService
必要なものが複数ある場合、またはさらに悪いことに、GroupMembershipService
で呼び出す必要があるいくつかの初期化「セッター」があり、コードがかなり急速に成長し、PersonService
現在はに依存するだけでGroupMembershipService
なく、GroupMembershipService
依存します。さらに、へのリンケージGroupMembershipService
はにハードコーディングされてPersonService
いるため、「ダミー」にすることはできません。GroupMembershipService
テスト目的、またはアプリケーションのさまざまな部分で戦略パターンを使用するため。
Dependency Injectionを使用すると、GroupMembershipService
内でをインスタンス化する代わりPersonService
に、PersonService
コンストラクターに渡すか、プロパティ(ゲッターとセッター)を追加して、そのローカルインスタンスを設定します。つまり、PersonService
を作成する方法について心配する必要がなくなりGroupMembershipService
、指定されたものを受け入れて、それらを使用するだけです。これはまたのサブクラスであるものを意味しGroupMembershipService
、または実装は、GroupMembershipService
インタフェースがに「を注入」することができPersonService
、およびPersonService
変更について知る必要はありません。
受け入れられた答えは良いものですが、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の依存関係逆転原理(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クラスを与えるかです。そこで登場するのがDependency Injectionです。
Car gasCar = new Car(new GasEngine());
gasCar.Run();
Car electroCar = new Car(new ElectricityEngine());
electroCar.Run();
ここでは、基本的に、依存関係(エンジンインスタンス)をCarコンストラクターに注入(パス)します。これで、クラスのオブジェクトとその依存関係が疎結合になり、Carクラスを変更せずに新しいタイプのエンジンを簡単に追加できるようになりました。
Dependency Injectionの主な利点は、クラスにハードコーディングされた依存関係がないため、クラスがより疎結合になることです。これは、前述の依存関係の逆転の原則に従います。特定の実装を参照する代わりに、クラスは、クラスの構築時に提供される抽象化(通常はインターフェイス)を要求します。
したがって、結局のところ、依存性注入は、オブジェクトとその依存関係の間の疎結合を実現するための手法にすぎません。クラスがそのアクションを実行するために必要とする依存関係を直接インスタンス化するのではなく、依存関係はコンストラクター注入によってクラスに(ほとんどの場合)提供されます。
また、多くの依存関係がある場合、すべての依存関係のどの具体的な実装にどのインターフェイスをマップする必要があるかを通知でき、構築時にこれらの依存関係を解決させることができるInversion of Control(IoC)コンテナーを使用することは非常に良い習慣です私たちのオブジェクト。たとえば、IoCコンテナのマッピングで、IEngine依存関係をGasEngineクラスにマッピングするように指定できます。IoCコンテナにCarクラスのインスタンスを要求すると、GasEngine依存関係を持つCarクラスが自動的に構築されます。渡された。
更新: Julie LermanのEF Coreに関するコースを最近視聴し、DIについての彼女の短い定義も気に入りました。
依存性注入とは、アプリケーションがオブジェクトをその場で必要なクラスに注入できるようにするためのパターンであり、それらのクラスにオブジェクトの責任を負わせる必要はありません。これにより、コードをより疎結合にし、Entity Framework Coreをこの同じサービスシステムにプラグインできます。
あなたが釣りに行きたいと想像しましょう:
依存性注入なしでは、すべてを自分で処理する必要があります。ボートを見つけたり、釣り竿を買ったり、餌を探したりする必要があります。もちろん可能ですが、それはあなたに多くの責任を負わせます。ソフトウェアの用語では、それはあなたがこれらすべてのもののルックアップを実行しなければならないことを意味します。
依存関係注入により、他の誰かがすべての準備を担当し、必要な機器を利用できるようにします。ボート、釣り竿、餌が届きます(「注射される」)。すべてすぐに使用できます。
これは、依存性注入と依存性注入コンテナに関する最も簡単な説明です。
依存性注入と依存性注入コンテナは異なります。
依存性注入を行うためにコンテナは必要ありません。ただし、コンテナが役立ちます。
「依存性注入」とは、パラメーター化されたコンストラクターとパブリックセッターを使用することだけではないのですか。
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(); ... } }
new DatabaseThingie()
有効なmyDatabaseインスタンスを生成しない場合のみ。
依存性注入の概念を理解しやすくするため。電球を切り替える(オン/オフ)ためのスイッチボタンの例を見てみましょう。
スイッチは、接続されている電球を事前に知っておく必要があります(ハードコードされた依存関係)。そう、
スイッチ-> PermanentBulb //スイッチは永久電球に直接接続されているため、テストは簡単にはできません
Switch(){
PermanentBulb = new Bulb();
PermanentBulb.Toggle();
}
スイッチは、渡された電球のオン/オフを切り替える必要があることだけを認識します。そう、
スイッチ-> Bulb1 OR Bulb2 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)とは何ですか?
他の人が言ったように、Dependency Injection(DI)は、関心のあるクラス(コンシューマークラス)が(UMLの意味で)依存している他のオブジェクトインスタンスの直接的な作成と寿命の管理の責任を取り除きます。これらのインスタンスは代わりに、通常はコンストラクターのパラメーターとして、またはプロパティセッターを介してコンシューマークラスに渡されます(依存関係オブジェクトのインスタンス化とコンシューマークラスへの受け渡しの管理は、通常、制御の反転(IoC)コンテナーによって実行されますが、それは別のトピックです)。 。
DI、DIP、SOLID
具体的には、ロバート・C・マーティンさんのパラダイムで設計オブジェクト指向のSOLID原則、DI
の可能な実装の1つである依存関係逆転の原則(DIP) 。DIPがあるD
のSOLID
マントラ -他のDIP実装はサービスロケータ、およびプラグインのパターンを含みます。
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)を使用する理由
この方法でクラス間の依存関係を分離すると、これらの依存関係クラスを、抽象化の前提条件を満たす他の実装で簡単に置き換えることができます(たとえば、同じインターフェイスの別の実装で依存関係を切り替えることができます)。さらに、他の人が述べたように、DIPを介してクラスを分離する最も一般的な理由は、これらの同じ依存関係をスタブまたはモックすることができるため、消費クラスを分離してテストできるようにすることです。
DIの結果の1つは、依存関係オブジェクトが(コンストラクターまたはセッターインジェクションを介して)消費クラスに渡されるようになるため、依存オブジェクトインスタンスの寿命管理が消費クラスによって制御されなくなることです。
これはさまざまな方法で表示できます。
Create
、必要に応じて、工場のa を介してインスタンスを取得し、完了したらこれらのインスタンスを破棄できます。いつDIを使用するのですか?
MyDepClass
スレッドセーフです。シングルトンにして同じインスタンスをすべてのコンシューマに注入するとどうなるでしょうか?)例
これが単純なC#の実装です。以下の消費クラスがあるとします。
public class MyLogger
{
public void LogRecord(string somethingToLog)
{
Console.WriteLine("{0:HH:mm:ss} - {1}", DateTime.Now, somethingToLog);
}
}
一見無害なように見えstatic
ますが、他の2つのクラスとに2 つの依存関係がSystem.DateTime
ありますSystem.Console
。これは、ログ出力オプションを制限するだけでなく(誰にも監視されていない場合、コンソールへのログ記録は無意味です)、さらに悪いことに、非決定論的なシステムクロック。
ただしDIP
、タイムスタンプの問題を依存関係として抽象化し、MyLogger
単純なインターフェースのみに結合することで、このクラスに適用できます。
public interface IClock
{
DateTime Now { get; }
}
のConsole
ような抽象化への依存を緩和することもできTextWriter
ます。Dependency Injectionは通常、constructor
インジェクション(依存関係への抽象化をパラメーターとして使用クラスのコンストラクターにSetter Injection
渡す)または(定義済みのsetXyz()
セッターまたは.Netプロパティを介して依存関係を渡す)として実装されます{set;}
。これは、クラスが構築後に正しい状態にあることを保証し、内部依存関係フィールドを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コンテナー(IoC)に関連付けられています。構成/ブートストラッププロセス中に、IoC
コンテナでは以下を定義できます。
IBar
、ConcreteBar
インスタンスを返す」)IDisposable
の責任を引き受けDisposing
ます。通常、IoCコンテナーが構成/ブートストラップされると、バックグラウンドでシームレスに動作し、コーダーは依存関係を気にすることなく手元のコードに集中できます。
DIフレンドリーなコードの鍵は、クラスの静的結合を回避し、依存関係の作成にnew()を使用しないことです
上記の例のように、依存関係の分離にはある程度の設計作業が必要です。開発者にとって、new
依存関係を直接使用する習慣を打ち破り、代わりにコンテナーを信頼して依存関係を管理するというパラダイムシフトが必要です。
しかし、特に関心のあるクラスを徹底的にテストする機能には、多くの利点があります。
注:生成/マッピング/投影(VIA new ..()
POCO / POJO /シリアル化のDTO /エンティティグラフ/匿名JSON突起ら) -すなわち、「データのみ」クラスまたはレコード-使用または方法から返さはされないで(依存性とみなしUMLセンス)、DIの対象ではありません。を使用new
してこれらを投影するだけで問題ありません。
依存性注入(DI)の要点は、アプリケーションのソースコードをクリーンで安定した状態に保つことです。
実際には、すべての設計パターンは、将来の変更が最小限のファイルに影響を与えるように、懸念を分離します。
DIの特定のドメインは、依存関係の構成と初期化の委任です。
Java以外で作業することsource
がある場合は、多くのスクリプト言語(Shell、Tclなど、またはimport
この目的で誤用されているPython でも)がどのように頻繁に使用されているかを思い出してください。
簡単な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
この場合に使用):
#!/bin/sh
# Dependency
function archive_files {
zip files.zip "$@"
}
source
依存スクリプトで直接実装スクリプトを実行する代わりに、injector.sh
両方の「コンポーネント」をラップする「コンテナ」を使用します。
#!/bin/sh
# Injector
source ./archive_files_zip.sh
source ./dependent.sh
archive_files
依存関係がちょうどされた注射さに依存するスクリプト。
またはarchive_files
を使用して実装する依存関係を注入した可能性があります。tar
xz
dependent.sh
スクリプトが依存関係を直接使用する場合、このアプローチは依存関係ルックアップと呼ばれます(依存関係注入とは逆です)。
#!/bin/sh
# Dependent
# dependency look-up
source ./archive_files_zip.sh
touch "one.txt" "two.txt"
archive_files "one.txt" "two.txt"
ここで問題は、依存する「コンポーネント」が初期化自体を実行する必要があることです。
依存関係の初期化を変更するたびに、「コンポーネント」のソースコードファイルの新しいリリースも必要になるため、「コンポーネント」のソースコードはクリーンでも安定でもありません。
DIは、Javaフレームワークほど大きくは強調されておらず、普及していません。
しかし、それは以下の懸念を分ける一般的なアプローチです。
依存関係のルックアップでのみ構成を使用しても、依存関係ごとに構成パラメーターの数が変わる可能性があるため(新しい認証タイプなど)、サポートされる依存関係のタイプの数(新しいデータベースタイプなど)が変わるため、効果がありません。
上記の答えはすべて良好です。私の目的は、プログラミングの知識がない人でもコンセプトを理解できるように、コンセプトを簡単に説明することです
依存性注入は、複雑なシステムをより簡単に作成するのに役立つ設計パターンの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();
}
あなたも読むことができます
Dependency Injection(DI)は、相互に依存しているオブジェクトを分離することを意味します。オブジェクトAがオブジェクトBに依存しているとしましょう。そのため、これらのオブジェクトを互いに分離することが考えられます。コンパイル時にもかかわらず、実行時にオブジェクトへの依存関係を共有するのではなく、新しいキーワードを使用してオブジェクトをハードコーディングする必要はありません。話したら
新しいキーワードを使用してオブジェクトをハードコードする必要はなく、設定ファイルでBeanの依存関係を定義します。春のコンテナは、すべてをフックする責任があります。
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"/>
注:必須の依存関係にはコンストラクター引数を使用し、オプションの依存関係にはセッターを使用することをお勧めします。セッターで@Requiredアノテーションに基づくアノテーションを使用する場合は、必須の依存関係としてセッターを作成するために使用できることに注意してください。
私が考えることができる最もよい類推は、手術室での外科医とその助手であり、外科医は、外科医が必要なときにさまざまな外科用コンポーネントを提供して、外科医が集中することができるように、さまざまな外科用コンポーネントを提供します彼が最善を尽くすこと(手術)。アシスタントがなければ、外科医はコンポーネントが必要になるたびに自分でコンポーネントを入手する必要があります。
略してDIは、コンポーネントに提供することにより、依存コンポーネントをフェッチするためにコンポーネントに共通する追加の責任(負担)を削除する手法です。
DIは、のように単一責任(SR)の原則に近づけますsurgeon who can concentrate on surgery
。
DIを使用する場合:ほとんどすべてのプロダクションプロジェクト(小規模/大規模)、特に常に変化するビジネス環境でDIを使用することをお勧めします:)
理由:変更をすばやくテストして市場にプッシュできるように、コードを簡単にテスト可能またはモック可能にする必要があるためです。さらに、あなたがより多くのコントロールを持っているコードベースへの旅であなたをサポートする素晴らしい無料のツール/フレームワークがたくさんあるのになぜあなたはそうしないでしょうか?
例として、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
コンストラクターを変更するときは、すべての場所でService
オブジェクトを作成するコードを変更する必要があります方法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
コンストラクタを変更するのは適切ではありません。それを防ぐために、
1)のようなDIインジェクターを使用することができます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
、インジェクタークラスで変更するだけですConstructor Injection
、のコンストラクターを見るとClient
、Client
クラスの依存関係の数がわかります短所
Constructor Injection
と、Service
作成時にオブジェクトが作成されますがClient
、Client
クラスの関数を使用せずに使用するService
ことがあるので、作成Service
は無駄になりますhttps://en.wikipedia.org/wiki/Dependency_injection
依存関係は、使用できるオブジェクトです(
Service
)
インジェクションは、依存関係(Service
)を、Client
それを使用する依存オブジェクト()に渡すことです
つまり、オブジェクトには、ジョブを実行するために必要な数の依存関係のみがあり、依存関係は少ないはずです。さらに、オブジェクトの依存関係は、可能であれば、「具体的な」オブジェクトではなく、インターフェース上にある必要があります。(具体的なオブジェクトとは、キーワードnewで作成されたオブジェクトです。)疎結合は、再利用性と保守性を高め、高価なサービスの代わりに「模擬」オブジェクトを簡単に提供できるようにします。
「依存性注入」(DI)は「制御の反転」(IoC)とも呼ばれ、この疎結合を促進する手法として使用できます。
DIを実装するには、主に2つの方法があります。
これは、オブジェクトの依存関係をコンストラクターに渡す手法です。
コンストラクタは具象オブジェクトではなくインターフェースを受け入れることに注意してください。また、orderDaoパラメータがnullの場合、例外がスローされることに注意してください。これは、有効な依存関係を受け取ることの重要性を強調しています。私の考えでは、コンストラクター注入は、オブジェクトに依存関係を与えるための好ましいメカニズムです。適切な実行のために「Person」オブジェクトにどの依存関係を付与する必要があるかをオブジェクトを呼び出している間、開発者には明らかです。
しかし、次の例を考えてみましょう。依存関係のない10個のメソッドを持つクラスがあり、IDAOに依存する新しいメソッドを追加するとします。コンストラクターインジェクションを使用するようにコンストラクターを変更することもできますが、これにより、あらゆる場所ですべてのコンストラクター呼び出しを変更しなければならない場合があります。または、依存関係を取得する新しいコンストラクターを追加するだけで、開発者は、一方のコンストラクターをもう一方のコンストラクターをいつ使用するかを簡単に知ることができます。最後に、依存関係の作成に非常にコストがかかる場合、めったに使用されない可能性があるのに、なぜ作成してコンストラクターに渡す必要があるのでしょうか。「セッター注入」は、このような状況で使用できる別のDIテクニックです。
セッターインジェクションでは、依存関係がコンストラクターに強制的に渡されることはありません。代わりに、依存関係は、必要なオブジェクトによって公開されるパブリックプロパティに設定されます。以前に暗示されたように、これを行うための主な動機は次のとおりです。
上記のコードの例を次に示します。
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;
誰もがDIのために書いたので、いくつか質問させてください。
これは、@ Adam Nが投稿した回答に基づいています。
なぜPersonServiceはGroupMembershipServiceについて心配する必要がないのですか?あなたは、GroupMembershipが依存する複数のもの(オブジェクト/プロパティ)を持っていると述べました。PServiceでGMServiceが必要な場合は、プロパティとして使用します。それを注入したかどうかに関係なく、それをあざけることができます。GMServiceに注入したいのは、GMServiceがより具体的な子クラスを持っている場合だけです。子クラスは実行時までわかりません。次に、サブクラスを注入します。または、それをシングルトンまたはプロトタイプとして使用したい場合。正直に言うと、構成ファイルには、コンパイル時に挿入する型(インターフェイス)のサブクラスまで、すべてハードコーディングされています。
編集する
DIでのJose Maria Arranzによる素晴らしいコメント
DIは、依存関係の方向を決定してグルーコードを記述する必要をなくすことにより、まとまりを高めます。
いいえ。依存関係の方向はXML形式または注釈としてあり、依存関係はXMLコードと注釈として記述されます。XMLと注釈はソースコードです。
DIは、すべてのコンポーネントをモジュール化(つまり、交換可能)することで結合を減らし、相互に明確に定義されたインターフェースを備えています。
いいえ。インターフェースに基づいてモジュール式コードを作成するために、DIフレームワークは必要ありません。
置換可能について:非常にシンプルな.propertiesアーカイブとClass.forNameを使用して、変更できるクラスを定義できます。コードの任意のクラスを変更できる場合、Javaは適切ではありません。スクリプト言語を使用してください。ちなみに、アノテーションは再コンパイルしないと変更できません。
私の意見では、DIフレームワークの唯一の理由は、ボイラープレートの削減です。よくできたファクトリシステムを使用すると、好みのDIフレームワークと同じ、より制御された、より予測可能なものを実行できます。DIフレームワークはコードの削減を約束します(XMLと注釈もソースコードです)。問題は、このボイラープレートの削減が非常に単純なケース(クラスごとに1つのインスタンスなど)で実際に行われることであり、現実の世界では、適切なサービスオブジェクトを選択することが、クラスをシングルトンオブジェクトにマッピングするほど簡単ではない場合があります。
人気のある回答は役に立たない方法で依存性注入を定義しているため、役に立ちません。「依存性」とは、オブジェクト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か所にリストすることですそれらをコーダーから非表示にすることです(コーダーに提供させるためではありません)。
通常、中間レベルは、構築されたオブジェクトのファクトリを提供します。ファクトリは、要求された各オブジェクトタイプが満たす必要があるロールを提供する必要があります。これは、構造の詳細を隠す中間レベルを設定することにより、工場によって課される抽象化のペナルティがすでに発生しているため、工場を使用することもできます。
私はすでに多くの答えがあることを知っていますが、これは非常に役に立ちました: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
インスタンス化がコンストラクターに移動されていることに注意してください。コンストラクタは、が必要とする4つの値である4つのパラメータを取りますDataSourceImpl
。けれどもMyDao
このクラスは、まだこれらの四つの値、それはもはや満足し、これらの依存関係自体に依存します。これらは、MyDao
インスタンスを作成するクラスによって提供されます。
依存関係の注入は、一般に「依存関係の難読化」要件と呼ばれる可能性のあるものに対する1つの可能なソリューションです。依存関係の難読化は、依存関係を必要とするクラスに依存関係を提供するプロセスから「明白な」性質を取り除き、何らかの方法で、その依存関係をそのクラスに提供することを難読化する方法です。これは必ずしも悪いことではありません。実際、依存関係がクラスに提供される方法を難読化することにより、クラス外の何かが依存関係の作成を担当します。つまり、さまざまなシナリオで、変更を行わずに依存関係の異なる実装をクラスに提供できます。クラスに。これは、プロダクションモードとテストモードの切り替え(たとえば、「模擬」サービスの依存関係の使用)に最適です。
残念なことに、一部の人々は、依存関係の難読化を行うには特殊なフレームワークが必要であると想定しているため、特定のフレームワークを使用しないことを選択した場合、どういうわけか「より少ない」プログラマーになります。多くの人が信じているもう1つの非常に不安な神話は、依存性注入が依存性難読化を実現する唯一の方法であるということです。これは明白かつ歴史的に明らかに100%間違っていますが、依存関係の難読化の要件には依存関係の注入に代わるものがあることを一部の人々に納得させるのに苦労します。
プログラマーは何年にもわたって依存関係の難読化の要件を理解しており、依存関係の注入が考案される前と後の両方で、多くの代替ソリューションが進化してきました。ファクトリパターンがありますが、特定のインスタンスへの注入が不要なThreadLocalを使用する多くのオプションもあります。依存関係は、便利な静的ゲッターメソッドを介してオブジェクトを利用できるようにするという利点があるスレッドに効果的に注入されます。ます。それを必要とするクラスに注釈を追加し、それを実現するために複雑なXML「接着剤」を設定する必要なしに、それを必要とするクラス。永続性(JPA / JDOなど)に依存関係が必要な場合、「透過的な永続性」を簡単に実現でき、POJOだけで構成されたドメインモデルとビジネスモデルクラスを使用できます(つまり、フレームワーク固有ではなく、アノテーションにロックされません)。
本から、 ' 整然としたJava開発者:Java 7の重要なテクニックとポリグロットプログラミング
DIはIoCの特定の形式であり、依存関係を見つけるプロセスは、現在実行中のコードの直接制御の外にあります。
技術的な説明に進む前に、最初に実際の例を使って視覚化します。依存関係の注入を学ぶための技術的な知識はたくさんありますが、私のような人々はそのコアコンセプトを理解できない最長時間です。
最初の画像では、たくさんの団結を持つ自動車工場があると仮定します。車は実際には組み立てユニットに組み込まれていますが、エンジン、シート、ホイールが必要です。したがって、アセンブリユニットはこれらすべてのユニットに依存し、それらは依存関係です。工場のです。
現在、この工場ですべてのタスクを維持するのは複雑すぎると感じます。メインタスク(アセンブリユニットでの車の組み立て)とともに、他のユニットにも集中する必要があるためです。です。現在、維持するのに非常に費用がかかり、工場の建物は巨大であるため、家賃に余分な費用がかかります。
次に、2番目の画像を見てください。ホイール、シート、エンジンを自己生産コストよりも安く提供するプロバイダー企業を見つけたら、今では工場でそれらを作る必要はありません。あなたは今あなたの組立ユニットのためだけに小さな建物を借りることができますので、メンテナンス作業が軽減され、余分なレンタルコストを削減できます。これで、メインタスク(車の組み立て)のみに集中することもできます。
これで、自動車を組み立てるためのすべての依存関係がプロバイダーから工場に注入されたと言えます。これは実際の依存性注入(DI)の例ですです。
技術用語では、依存性注入とは、あるオブジェクト(または静的メソッド)が別のオブジェクトの依存関係を提供する手法です。したがって、オブジェクトを作成するタスクを他の人に転送し、依存関係を直接使用することを、依存関係注入と呼びます。
Book Apress.Spring.Persistence.with.Hibernate.Oct.2010より
依存関係の挿入の目的は、アプリケーションのビジネスロジックから外部ソフトウェアコンポーネントを解決する作業を切り離すことです。依存関係の挿入がないと、コンポーネントが必要なサービスにアクセスする方法の詳細が、コンポーネントのコードと混同される可能性があります。これは、エラーの可能性を増大させるだけでなく、コードの肥大化を追加し、メンテナンスの複雑さを増大させます。コンポーネントをより密接に結合し、リファクタリングまたはテスト時に依存関係を変更することを困難にします。
Dependency Injection(DI)は、OOPの基本機能である、あるオブジェクトと別のオブジェクトの関係を使用するデザインパターンの1つです。継承は1つのオブジェクトを継承してより複雑で特定の別のオブジェクトを実行しますが、関係または関連付けは、属性を使用して1つのオブジェクトから別のオブジェクトへのポインタを作成するだけです。DIの機能は、インターフェイスや隠しコードと同様に、OOPの他の機能と組み合わされています。図書館に顧客(購読者)がいて、簡単にするために1冊しか借りられないとします。
本のインターフェース:
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();
}
次に、さまざまな種類の本を用意できます。タイプの1つはフィクションです。
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がInversion of Control(IoC)と緊密に結びついている理由です。つまり、プログラムは別の実行中のモジュールに制御を渡し、コードへのBeanの注入を行います。(注入可能な各オブジェクトは、署名またはBeanと見なすことができます。)たとえば、Springでは、作成と初期化によって行われます。 ApplicationContextをます。これは私たちのために機能しますコンテナ。コードで単純にコンテキストを作成し、Beanの初期化を呼び出します。その瞬間、注入は自動的に行われました。
Christoffer Noring著、Pablo Deelemanの本「Learning Angular-Second Edition」:
「アプリケーションが成長し進化するにつれて、コードエンティティのそれぞれが内部で他のオブジェクトのインスタンスを必要とします。これは、ソフトウェアエンジニアリングの世界では依存関係としてよく知られています。このような依存関係を依存型クライアントに渡すアクションは、インジェクションと呼ばれます。また、別のコードエンティティ(インジェクター)の参加も伴います。インジェクターはインスタンス化とブートストラップを担当します、必要な依存関係のそのため、クライアントに正常に挿入されたその瞬間から使用できるようになります。クライアントは自身の依存関係をインスタンス化する方法について何も知らず、インターフェースのみを認識しているため、これは非常に重要ですそれらを使用するために実装です。」
投稿者:アントンモイセエフ。本「Angular Development with Typescript、Second Edition」:
「要するに、DIは、あなたがコードを書くことができます疎結合な方法と、あなたなりのコードがより多くのテスト可能と再利用可能。」
依存関係の注入は、Spring Frameworkに関連する概念の中心です。任意のプロジェクトの春のフレームワークを作成することは、重要な役割を果たす可能性があります。ここで、依存関係の注入はピッチャーで行われます。
実際、JavaでクラスAとクラスBの2つの異なるクラスを作成し、クラスAで使用したいクラスBで使用できる関数があるとすると、その時点で依存性注入を使用できます。あるクラスのオブジェクトを別のクラスに作成できるのと同じように、クラス全体を別のクラスに注入してアクセス可能にします。これにより、依存関係を克服できます。
依存性注入は、2つのクラスを単純に接着し、同時に、それらを別々に保持します。