依存性注入とは何ですか?
-
02-07-2019 - |
質問
すでにいくつかの質問が投稿されており、具体的な質問が含まれています 依存性注入, 、いつ使用するか、どのようなフレームワークがあるかなど。しかし、
依存関係の注入とは何ですか?いつ使用する必要があるのか、使用すべきでない場合とその理由は何ですか?
解決
依存関係の注入 依存関係を他のものに渡しています オブジェクト または フレームワーク(依存性インジェクター)。
依存関係の注入によりテストが容易になります。注射は次のように行うことができます。 コンストラクタ.
SomeClass()
そのコンストラクターは次のとおりです。
public SomeClass() {
myObject = Factory.getObject();
}
問題:もし myObject
ディスクアクセスやネットワークアクセスなどの複雑なタスクが含まれる場合、 難しい 単体テストを行うには SomeClass()
. 。プログラマはモックする必要がある myObject
そしてかもしれない インターセプト 工場の呼び出し。
代替ソリューション:
- 通過
myObject
コンストラクターへの引数として
public SomeClass (MyClass myObject) {
this.myObject = myObject;
}
myObject
直接渡すことができるため、テストが容易になります。
- 一般的な代替手段の 1 つは、 何もしないコンストラクター. 。依存関係の注入はセッターを通じて実行できます。(h/t @MikeVella)。
- マーティン・ファウラー 3 番目の代替案 (h/t @MarcDix) を文書化します。 クラスはインターフェースを明示的に実装します プログラマが注入したい依存関係用。
依存関係の注入を行わない単体テストでコンポーネントを分離することは困難です。
2013 年に私がこの回答を書いたとき、これは重要なテーマでした。 Google テスト ブログ. 。プログラマーはランタイム設計 (サービス ロケーターや同様のパターンなど) に特別な柔軟性を常に必要としているわけではないため、これが私にとって最大の利点であることに変わりはありません。プログラマーは多くの場合、テスト中にクラスを分離する必要があります。
他のヒント
私がこれまでに見つけた最良の定義は次のとおりです ジェームス・ショアの作品:
「依存関係注射」は、5セントの概念の25ドルの用語です。...]依存性噴射とは、オブジェクトにインスタンス変数を与えることを意味します。[...]。
がある マーティン・ファウラーによる記事 それも役立つかもしれません。
依存関係の注入は基本的に、オブジェクト自体がオブジェクトを構築するのではなく、オブジェクトが必要とするオブジェクト (その依存関係) を提供します。これは依存関係をモック化またはスタブ化できるため、テストに非常に便利な手法です。
依存関係は、さまざまな方法 (コンストラクター注入やセッター注入など) でオブジェクトに注入できます。特殊な依存関係注入フレームワークを使用することもできます (例:Spring) を使用しますが、確かに必須ではありません。これらのフレームワークに依存関係の注入を行う必要はありません。オブジェクト (依存関係) を明示的にインスタンス化して渡すことは、フレームワークによるインジェクションと同じくらい優れたインジェクションです。
この面白い例を見つけました。 疎結合:
どのアプリケーションも、相互に連携して便利な機能を実行する多くのオブジェクトで構成されています。従来、各オブジェクトは、連携する依存オブジェクト (依存関係) への独自の参照を取得する責任があります。これにより、クラスが高度に結合され、コードのテストが困難になります。
たとえば、次のことを考えてみましょう。 Car
物体。
あ Car
ホイール、エンジン、燃料、バッテリーなどによって異なります。走る。従来、このような依存オブジェクトのブランドを、 Car
物体。
依存性注入 (DI) を使用しない場合:
class Car{
private Wheel wh = new NepaliRubberWheel();
private Battery bt = new ExcideBattery();
//The rest
}
ここで、 Car
物体 依存オブジェクトの作成を担当します。
依存オブジェクトのタイプを変更したい場合はどうすればよいですか - たとえば Wheel
- イニシャルの後 NepaliRubberWheel()
パンク?新しい依存関係を使用して 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;
}
}
ソース: 依存関係の注入について理解する
依存関係の挿入は、オブジェクトを内部で構築するのではなく、他のコード部分からオブジェクトのインスタンスを受け取る方法でオブジェクトを設計する手法です。これは、オブジェクトに必要なインターフェイスを実装する任意のオブジェクトをコードを変更せずに置き換えることができることを意味し、これによりテストが簡素化され、分離が改善されます。
たとえば、次のクラスについて考えてみましょう。
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
その仕事をするために。依存関係の注入を使用しない場合、これを行う従来の方法は、新しいインスタンスを作成することになります。 GroupMembershipService
のコンストラクター内で PersonService
そしてそのインスタンス属性を両方の関数で使用します。ただし、コンストラクターが GroupMembershipService
必要なものが複数あり、さらに悪いことに、 GroupMembershipService
, 、コードはかなり急速に大きくなり、 PersonService
今、依存しているのは、 GroupMembershipService
だけでなく、その他すべてのことも GroupMembershipService
に依存します。さらに、との連動は、 GroupMembershipService
にハードコードされています PersonService
つまり、「ダミー」はできないということです。 GroupMembershipService
テスト目的、またはアプリケーションのさまざまな部分で戦略パターンを使用する場合。
依存関係の注入を使用すると、インスタンスを作成する代わりに、 GroupMembershipService
あなたの中で PersonService
, に渡すか、 PersonService
コンストラクターを使用するか、プロパティ (ゲッターとセッター) を追加してそのローカル インスタンスを設定します。これは、あなたの PersonService
の作成方法について心配する必要はもうありません。 GroupMembershipService
, 、与えられたものを受け入れるだけで、それらを使用して動作します。これは、次のサブクラスであるものはすべて、 GroupMembershipService
, 、または実装します GroupMembershipService
インターフェースを「注入」することができます。 PersonService
, 、 そしてその PersonService
変化について知る必要はありません。
受け入れられた答えは良いものですが、DI はコード内でハードコードされた定数を回避する古典的な方法に非常によく似ていることを付け加えておきたいと思います。
データベース名のような定数を使用する場合は、それをコード内から構成ファイルにすぐに移動し、その値を含む変数を必要な場所に渡します。そうする理由は、これらの定数は通常、コードの残りの部分よりも頻繁に変更されるためです。たとえば、テスト データベースでコードをテストしたい場合です。
DI は、オブジェクト指向プログラミングの世界ではこれに似ています。定数リテラルの代わりにそこにある値はオブジェクト全体です。ただし、値を作成するコードをクラス コードから移動する理由は同様です。オブジェクトは、それを使用するコードよりも頻繁に変更されます。このような変更が必要となる重要なケースの 1 つはテストです。
簡単な例を試してみましょう 車 そして エンジン 少なくとも現時点では、どんな車でもどこに行くにもエンジンが必要です。以下に、依存関係注入を行わない場合のコードの様子を示します。
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 や ElectricalEngine などの実際の具体的な Engine クラスを与える方法です。そこです 依存関係の注入 入って来る。
Car gasCar = new Car(new GasEngine());
gasCar.Run();
Car electroCar = new Car(new ElectricityEngine());
electroCar.Run();
ここでは基本的に、依存関係 (エンジン インスタンス) を Car コンストラクターに注入 (渡し) します。これで、クラスのオブジェクトとその依存関係が疎結合になり、Car クラスを変更せずに新しいタイプのエンジンを簡単に追加できるようになりました。
主な利点は、 依存関係の注入 クラスにはハードコーディングされた依存関係がないため、より疎結合であることがわかります。これは、上で説明した依存関係逆転の原則に従います。特定の実装を参照する代わりに、クラスは抽象化を要求します (通常は インターフェース) クラスの構築時に提供されます。
それで結局のところ 依存関係の注入 オブジェクトとその依存関係の間のゆるい結合を達成するためのテクニックです。クラスがアクションを実行するために必要な依存関係を直接インスタンス化するのではなく、コンストラクター噴射を介してクラスに(ほとんどの場合)依存関係が提供されます。
また、多くの依存関係がある場合、Inversion of Control(IoC) コンテナーを使用することは非常に良い習慣です。これにより、すべての依存関係に対してどのインターフェイスをどの具体的な実装にマップする必要があるかを判断でき、構築時にそれらの依存関係を解決させることができます。私たちの目的。たとえば、IoC コンテナのマッピングで次のように指定できます。 Iエンジン 依存関係はにマッピングする必要があります ガスエンジン クラスを作成し、IoC コンテナにそのインスタンスを要求するとき 車 クラスを作成すると、自動的に構築されます 車 のクラス ガスエンジン 依存関係が渡されました。
アップデート: 最近、Julie Lerman による EF Core に関するコースを視聴し、DI に関する彼女の短い定義も気に入りました。
依存関係の注入は、アプリケーションがそれらを必要とするクラスにオブジェクトをその場で挿入できるようにするパターンです。これにより、コードがよりゆるく結合されるようになり、エンティティフレームワークはこの同じサービスシステムにプラグインします。
釣りに行きたいと想像してみましょう。
依存関係の注入を行わない場合は、すべてを自分で行う必要があります。ボートを見つけたり、釣り竿を買ったり、餌を探したりする必要があります。もちろんそれは可能ですが、それには多くの責任が伴います。ソフトウェア用語で言えば、これらすべての検索を実行する必要があることを意味します。
依存関係の注入では、他の誰かがすべての準備を担当し、必要な機器を利用できるようにします。ボート、釣り竿、餌がすべて届くので、すぐに使用できます。
これ についての最も簡単な説明です 依存関係の注入 そして 依存性注入コンテナ 今まで見た中で:
依存関係の注入なし
- アプリケーションには Foo が必要です (例:コントローラー)、つまり:
- アプリケーションが Foo を作成する
- アプリケーションは Foo を呼び出します
- Foo には Bar が必要です (例:サービス)なので、次のようになります。
- Foo は Bar を作成します
- フーはバーを呼び出す
- BARにはBIM(サービス、リポジトリなどが必要です…)、だから:
- バーがBimを作成します
- バーが何かをする
依存関係の注入あり
- アプリケーションには Foo が必要で、アプリケーションには Bar が必要で、アプリケーションには Bim が必要です。つまり、次のようになります。
- アプリケーションが Bim を作成する
- アプリケーションは Bar を作成し、それに Bim を与えます
- アプリケーションは Foo を作成し、それに Bar を与えます
- アプリケーションは Foo を呼び出します
- フーはバーを呼び出す
- バーが何かをする
- フーはバーを呼び出す
依存関係注入コンテナーの使用
- アプリケーションには Foo が必要なので、次のようになります。
- アプリケーションはコンテナから Foo を取得するため、次のようになります。
- コンテナがBimを作成する
- コンテナはバーを作成し、それに Bim を与えます
- コンテナは Foo を作成し、それに Bar を与えます
- アプリケーションは Foo を呼び出します
- フーはバーを呼び出す
- バーが何かをする
- フーはバーを呼び出す
依存関係の注入 そして 依存関係の注入コンテナ は異なるものです:
- 依存関係の注入は、より良いコードを書くための方法です
- DI コンテナは依存関係の注入を支援するツールです
依存関係の注入を行うためにコンテナーは必要ありません。ただし、コンテナが役に立ちます。
「依存関係の注入」とは、パラメーター化されたコンストラクターとパブリック セッターを使用することだけを意味するのではありませんか?
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) とは何ですか?
他の人も言っているように、 依存性注入(DI) 対象のクラス (コンシューマ クラス) が依存する他のオブジェクト インスタンスの直接作成と存続期間の管理の責任を取り除きます ( UMLセンス)。これらのインスタンスは、代わりにコンシューマ クラスに、通常はコンストラクタ パラメータとして、またはプロパティ セッターを介して渡されます (依存関係オブジェクトのインスタンス化とコンシューマ クラスへの渡しの管理は、通常、 制御の反転 (IoC) コンテナですが、それは別のトピックです)。
DI、DIP、ソリッド
具体的には、ロバート C マーティンのパラダイムでは、 オブジェクト指向設計の堅固な原則, DI
可能な実装の 1 つです 依存関係逆転の原則 (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
必要に応じて工場で実行し、完了したらこれらのインスタンスを破棄します。 - または、依存関係インスタンスのライフスパン制御を IoC コンテナーに放棄することもできます (これについては以下で詳しく説明します)。
DIをいつ使用するか?
- 同等の実装を依存関係に置き換える必要がある可能性がある場合、
- 依存関係を分離してクラスのメソッドを単体テストする必要がある場合はいつでも、
- 依存関係の存続期間が不確実であるため、実験を行う必要がある場合 (例:おい、
MyDepClass
スレッドセーフです - シングルトンにして同じインスタンスをすべてのコンシューマーに注入したらどうなるでしょうか?)
例
ここでは簡単な C# 実装を示します。以下の Consuming クラスがあるとします。
public class MyLogger
{
public void LogRecord(string somethingToLog)
{
Console.WriteLine("{0:HH:mm:ss} - {1}", DateTime.Now, somethingToLog);
}
}
一見無害に見えますが、2つの要素があります。 static
他の 2 つのクラスへの依存関係、 System.DateTime
そして System.Console
, これにより、ロギング出力オプションが制限されるだけでなく (誰も監視していなければ、コンソールにロギングしても意味がありません)、さらに悪いことに、非決定的なシステム クロックへの依存関係を考慮すると、自動的にテストすることが困難になります。
ただし、申請することはできます DIP
タイムスタンプの問題を依存関係として抽象化し、結合することで、このクラスに追加します。 MyLogger
単純なインターフェイスのみ:
public interface IClock
{
DateTime Now { get; }
}
への依存を緩めることもできます。 Console
のような抽象化に TextWriter
. 。依存関係の注入は通常、次のいずれかとして実装されます。 constructor
インジェクション (消費クラスのコンストラクターへのパラメーターとして抽象化を依存関係に渡す) または Setter Injection
(依存関係を setXyz()
セッターまたは .Net プロパティ {set;}
定義されています)。コンストラクター インジェクションは、構築後にクラスが正しい状態になることを保証し、内部依存関係フィールドを次のようにマークできるため、推奨されます。 readonly
(C#) または final
(ジャワ)。したがって、上記の例でコンストラクター インジェクションを使用すると、次のようになります。
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());
}
次のステップ
依存関係の注入は常に、 コントロールコンテナ(IoC)の反転, 、具体的な依存関係インスタンスを挿入 (提供) し、ライフスパン インスタンスを管理します。構成/ブートストラッププロセス中に、 IoC
コンテナでは以下を定義できます。
- 各抽象化と設定された具体的な実装の間のマッピング (例: 「消費者がリクエストするたびに、
IBar
, 、を返しますConcreteBar
実例") - 各依存関係の存続期間管理のためにポリシーを設定できます。各コンシューマー インスタンスに新しいオブジェクトを作成する、すべてのコンシューマー間でシングルトン依存関係インスタンスを共有する、同じスレッド間でのみ同じ依存関係インスタンスを共有する、などです。
- .Net では、IoC コンテナは次のようなプロトコルを認識します。
IDisposable
そしてその責任を引き受けますDisposing
構成されたライフスパン管理に合わせて依存関係を管理します。
通常、IoC コンテナが構成/ブートストラップされると、バックグラウンドでシームレスに動作するため、プログラマーは依存関係を気にすることなく、手元のコードに集中できます。
DI に適したコードの鍵は、クラスの静的結合を回避し、依存関係の作成に new() を使用しないことです。
上記の例のように、依存関係の分離にはある程度の設計作業が必要であり、開発者にとっては、依存関係の習慣を断ち切るためにパラダイム シフトが必要です。 new
依存関係を直接管理するのではなく、コンテナーを信頼して依存関係を管理します。
ただし、特に関心のあるクラスを徹底的にテストできる点では、利点は数多くあります。
注記 :作成 / マッピング / プロジェクション (経由) new ..()
) POCO / POJO / シリアル化 DTO / エンティティ グラフ / 匿名 JSON プロジェクションなど - つまり「データのみ」のクラスまたはレコード - メソッドから使用または返されるものは、 ない (UML の意味で) 依存関係とみなされ、DI の対象にはなりません。使用する new
これらを投影するのは問題ありません。
依存性注入の概念を理解しやすくするため。電球を切り替える(オン/オフ)ためのスイッチ ボタンの例を見てみましょう。
依存関係の注入なし
スイッチは、どの電球に接続されているかを事前に知る必要があります (ハードコードされた依存関係)。それで、
スイッチ -> 常設電球 //スイッチは永久電球に直接接続されているため、簡単にテストすることはできません
Switch(){
PermanentBulb = new Bulb();
PermanentBulb.Toggle();
}
依存関係の注入あり
スイッチは、渡された電球をオン/オフにする必要があることだけを知っています。それで、
スイッチ -> Bulb1 OR Bulb2 OR NightBulb (注入された依存関係)
Switch(AnyBulb){ //pass it whichever bulb you like
AnyBulb.Toggle();
}
変更中 ジェームス スイッチと電球の例:
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) の要点は、アプリケーションのソース コードを保持することです。 クリーン そして 安定した:
- クリーン 依存関係の初期化コードの
- 安定した 使用されている依存関係に関係なく
実際には、すべての設計パターンで懸念事項が分離され、将来の変更が最小限のファイルに影響を与えるようになります。
DI の特定の領域は、依存関係の構成と初期化の委任です。
例:シェルスクリプトによるDI
時々 Java の外部で作業する場合は、その方法を思い出してください。 source
多くのスクリプト言語 (シェル、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
.
例:DIの削除
もし 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();
}
読むこともできます
依存性インジェクションとは何ですか?
依存性注入(DI)とは、相互に依存しているオブジェクトを切り離すことを意味します。オブジェクト A がオブジェクト B に依存しているとします。そのため、これらのオブジェクトを相互に分離することが考えられます。new キーワードを使用してオブジェクトをハードコーディングする必要はなく、コンパイル時にもかかわらず、実行時にオブジェクトへの依存関係を共有します。について話したら
Spring での依存関係の注入の仕組み:
new キーワードを使用してオブジェクトをハードコーディングする必要はなく、構成ファイルで Bean の依存関係を定義します。スプリング コンテナはすべてを接続する役割を果たします。
制御の反転 (IOC)
IOC は一般的な概念であり、さまざまな方法で表現できます。依存関係の注入は IOC の具体例の 1 つです。
2 種類の依存性注入:
- コンストラクターのインジェクション
- セッターインジェクション
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>
2.セッターベースの依存関係の注入:
Setter ベースの DI は、引数なしのコンストラクターまたは引数なしの静的ファクトリ メソッドを呼び出して Bean をインスタンス化した後、コンテナが Bean の setter メソッドを呼び出すことによって実現されます。
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 を使用することをお勧めします:)
なぜ :変更を迅速にテストして市場にプッシュできるように、コードを簡単にテストしたりモックしたりできるようにしたいからです。さらに、より詳細に制御できるコードベースへの移行をサポートする素晴らしい無料ツールやフレームワークがたくさんあるのに、なぜそうしないのでしょうか。
これは、オブジェクトがそのジョブを実行するために必要なだけの依存関係を持つ必要があり、依存関係は少数である必要があることを意味します。さらに、オブジェクトの依存関係は、可能な場合は「具体的な」オブジェクトではなく、インターフェイスに依存する必要があります。(具象オブジェクトとは、new キーワードを使用して作成されたオブジェクトです。) 疎結合により、再利用性が向上し、保守が容易になり、高価なサービスの代わりに「モック」オブジェクトを簡単に提供できるようになります。
「依存性注入」(DI) は「制御の反転」(IoC) としても知られており、この疎結合を促進する手法として使用できます。
DI を実装するには、主に 2 つのアプローチがあります。
- コンストラクターのインジェクション
- セッターインジェクション
コンストラクターのインジェクション
これは、オブジェクトの依存関係をコンストラクターに渡す手法です。
コンストラクターは具体的なオブジェクトではなくインターフェイスを受け入れることに注意してください。また、orderDao パラメータが null の場合は例外がスローされることに注意してください。これは、有効な依存関係を受け取ることの重要性を強調しています。私の意見では、コンストラクター インジェクションは、オブジェクトに依存関係を与えるための好ましいメカニズムです。開発者にとって、オブジェクトを呼び出すときに、適切に実行するにはどの依存関係を「person」オブジェクトに与える必要があるかは明らかです。
セッターインジェクション
ただし、次の例を考えてみましょう。依存関係のない 10 個のメソッドを持つクラスがあり、IDAO に依存関係がある新しいメソッドを追加するとします。コンストラクター インジェクションを使用するようにコンストラクターを変更することもできますが、これにより、あらゆる場所のすべてのコンストラクター呼び出しを変更する必要が生じる可能性があります。あるいは、依存関係を取得する新しいコンストラクターを追加することもできますが、その場合、開発者は、あるコンストラクターを他のコンストラクターではなくいつ使用するかを簡単に知ることができるのでしょうか。最後に、依存関係の作成に非常にコストがかかる場合、めったに使用されないにもかかわらず、なぜ依存関係を作成してコンストラクターに渡す必要があるのでしょうか?「セッターインジェクション」も、このような状況で使用できる DI テクニックの 1 つです。
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
コンストラクターでは、すべての場所でコードを変更する必要があります。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
コンストラクタ。それを防ぐために、次のような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) ライブラリを使用します:アンドロイド用 短剣2
利点
- テストを簡単にする
- 変更するときは、
Service
, 、Injector クラスでのみ変更する必要があります。 - 使用する場合は使用してください
Constructor Injection
, のコンストラクターを見ると、Client
, 、依存関係の数が表示されます。Client
クラス
短所
- 使用する場合は使用してください
Constructor Injection
, 、Service
オブジェクトは次のときに作成されますClient
作成され、時々関数を使用しますClient
使わないクラスService
そうやって作られたService
無駄だ
依存性注入の定義
https://en.wikipedia.org/wiki/Dependency_injection
依存関係は使用できるオブジェクトです (
Service
)
インジェクションは依存関係を渡すことです(Service
) から依存オブジェクト (Client
)それはそれを使用します
皆さんが DI に書いていると思うので、いくつか質問させてください。
- すべての実際の実装(インターフェイスではない)がクラス(たとえば、コントローラーへのサービス)に挿入されるDIの構成がある場合、なぜそれがある種のハードコーディングではないのですか?
- 実行時にオブジェクトを変更したい場合はどうすればよいですか?たとえば、私の構成では、MyController をインスタンス化するときに、FileLogger を ILogger として挿入するようにすでに指定されています。ただし、DatabaseLogger を挿入することもできます。
- AClass に必要なオブジェクトを変更するたびに、クラス自体と構成ファイルの 2 つの場所を調べる必要があります。それによってどのように生活が楽になるのでしょうか?
- AClass の Aprproperty が注入されていない場合、モックアウトするのは難しくなりますか?
- 最初の質問に戻ります。new object() の使用が不適切である場合、インターフェイスではなく実装を注入するのはなぜでしょうか?実際にはインターフェースを挿入しているが、構成によりそのインターフェースの実装を実行時ではなく指定するようになっている、と多くの人が言っていると思います。コンパイル時にハードコーディングされます。
これは、@Adam Nが投稿した回答に基づいています。
PersonService が GroupMembershipService について心配する必要がなくなったのはなぜですか?GroupMembership には複数のもの (オブジェクト/プロパティ) が依存していると述べました。PService で GMService が必要な場合は、それをプロパティとして持つことになります。あなたがそれを注入したかどうかに関係なく、それを模擬することができます。これを注入してほしいのは、GMService により具体的な子クラスがある場合だけですが、これは実行時までわかりません。次に、サブクラスを注入します。または、それをシングルトンまたはプロトタイプとして使用したい場合。正直に言うと、構成ファイルには、コンパイル時にタイプ (インターフェイス) のどのサブクラスを挿入するかについて、すべてがハードコーディングされています。
編集
DI は、依存関係の方向を決定し、グルー コードを記述する必要性を排除することで、凝集性を高めます。
間違い。依存関係の方向は XML 形式または注釈として指定され、依存関係は XML コードおよび注釈として記述されます。XML と注釈はソース コードです。
DI は、すべてのコンポーネントをモジュール化することでカップリングを軽減します (つまり、交換可能)、相互に明確に定義されたインターフェイスを備えています。
間違い。インターフェイスに基づいてモジュール式コードを構築するために DI フレームワークは必要ありません。
交換可能について:非常に単純な .properties アーカイブと Class.forName を使用して、変更できるクラスを定義できます。コードのいずれかのクラスを変更できる場合は、Java は適していません。スクリプト言語を使用してください。ところで:アノテーションは再コンパイルしないと変更できません。
私の意見では、DI フレームワークを使用する理由は 1 つだけです。ボイラープレートの削減。よくできたファクトリ システムを使用すると、好みの DI フレームワークと同じことを、より制御され、より予測可能に行うことができます。DI フレームワークはコード削減を約束します (XML と注釈もソース コードです)。問題は、この定型文の削減は非常に単純な場合 (クラスごとに 1 つのインスタンスなど) で実際に起こることですが、現実の世界では、適切なサービス オブジェクトを選択するのは、クラスをシングルトン オブジェクトにマッピングするほど簡単ではない場合があります。
依存関係の注入 方法を意味します(実際には ともかく) コードの一部 (例: クラス) の依存関係 (コードの他の部分、例: 依存する他のクラス) に、ハードコーディングすることなくモジュール方式でアクセスできるようにするため (したがって、依存関係を自由に変更したりオーバーライドしたりできます)必要に応じて、別の時点でロードされます)
(追記、はい、かなり単純なコンセプトに対して、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
インスタンス化はコンストラクターに移動されます。コンストラクターは 4 つのパラメーターを取ります。これらのパラメーターは、 DataSourceImpl
. 。しかし、 MyDao
class は依然としてこれら 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 を使用するオプションも多数あります。依存関係は効果的にスレッドに注入され、(便利な静的ゲッター メソッドを介して) オブジェクトを利用できるようにするという利点があります。 どれでも それを必要とするクラスにアノテーションを追加したり、それを実現するための複雑な XML の「接着剤」を設定したりする必要はありません。依存関係が永続性 (JPA/JDO など) に必要な場合、純粋に POJO で構成されるドメイン モデルとビジネス モデル クラスを使用して、「トラナペアレント永続性」をはるかに簡単に実現できます。フレームワーク固有のフレームワークはありません/アノテーションにロックされています)。
本より、「しっかりとした根拠のある Java 開発者:Java 7 と多言語プログラミングの重要なテクニック
DIはIOCの特定の形式であり、依存関係を見つけるプロセスは、現在実行中のコードの直接的な制御の外にあります。
本から Apress.Spring.Persistence.with.Hibernate.2010 年 10 月
依存噴射の目的は、アプリケーションビジネスロジックから外部ソフトウェアコンポーネントを解決する作業を分離することです。依存関係の注入がなければ、コンポーネントが必要なサービスにアクセスする方法の詳細は、コンポーネントのコードで混乱することができます。これにより、エラーの可能性が高まり、コードの膨らみが追加され、メンテナンスの複雑さが拡大されるだけではありません。コンポーネントをより密接に結合するため、リファクタリングまたはテスト時に依存関係を変更することが困難になります。
依存関係の挿入 (DI) はデザイン パターンの 1 つで、OOP の基本機能、つまりあるオブジェクトと別のオブジェクトの関係を使用します。継承では、あるオブジェクトを継承して、より複雑で具体的な別のオブジェクトを実行しますが、関係または関連付けでは、属性を使用して、あるオブジェクトから別のオブジェクトへのポインタを単純に作成します。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();
}
}
依存関係の注入にはさまざまな方法があります。Singletonなどと組み合わせることも可能ですが、やはり基本的には別のオブジェクト内にオブジェクト型の属性を作成することで実現される関連付けに過ぎません。有用性は機能にのみあり、何度も何度も書く必要があるコードが常に準備され、今後のために実行されます。これが、DI が制御の反転 (IoC) と密接に結びついている理由です。つまり、プログラムが別の実行中のモジュールに制御を渡し、コードに Bean を注入します。(注入できる各オブジェクトは、署名することも、Bean とみなすこともできます。) たとえば、Spring では、作成と初期化によって行われます。 アプリケーションコンテキスト コンテナーがこの作業を行ってくれます。コード内でコンテキストを作成し、Bean の初期化を呼び出すだけです。その瞬間、注入は自動的に行われます。
簡単に言えば、依存関係注入 (DI) は、異なるオブジェクト間の依存関係または密結合を削除する方法です。依存関係の注入は、各オブジェクトに一貫した動作を与えます。
DI は、「電話しないでください。電話します」という Spring の IOC プリンシパルの実装です。依存関係注入を使用すると、プログラマは new キーワードを使用してオブジェクトを作成する必要がありません。
オブジェクトは Spring コンテナにロードされると、必要なときにはいつでも getBean(String beanName) メソッドを使用して Spring コンテナからオブジェクトをフェッチすることで再利用します。
依存性注入は Spring Framework に関連する概念の中心です。プロジェクトのフレームワークを作成する際に Spring は重要な役割を果たしますが、ここでは依存性注入が重要な役割を果たします。
実際に、Java でクラス A とクラス B という 2 つの異なるクラスを作成し、クラス B で使用できる関数をクラス A で使用したいとします。そのとき、依存関係注入が使用できます。ここでは、あるクラスのオブジェクトを別のクラスでクレートすることができます。同様に、クラス全体を別のクラスに注入してアクセスできるようにすることができます。このようにして依存関係を克服することができます。
依存性の注入は、単に 2 つのクラスを結合し、同時にそれらを分離したままにするだけです。
依存関係の挿入 (DI) は、制御の反転 (IoC) とも呼ばれる依存関係反転原則 (DIP) 実践の一部です。基本的に、コードを 1 つのモノリシック システムではなく、よりモジュール化して単体テスト可能にしたいため、DIP を実行する必要があります。そこで、クラスから分離して抽象化できるコード部分を特定し始めます。ここで、抽象化の実装をクラスの外部から注入する必要があります。通常、これはコンストラクターを介して実行できます。したがって、抽象化をパラメータとして受け入れるコンストラクターを作成します。これは、依存関係注入 (コンストラクター経由) と呼ばれます。DIP、DI、および IoC コンテナーの詳細については、以下を参照してください。 ここ
私は、依存性注入とは何かについて、技術的手段ではなく主な目的に焦点を当てて、少し異なる、短く正確な定義を提案します (以下に続きます)。 ここ):
依存関係注射は、各サービスが依存関係によってパラメーター化されるサービスオブジェクトの静的なステートレスグラフを作成するプロセスです。
アプリケーションで作成するオブジェクト (Java、C#、またはその他のオブジェクト指向言語を使用するかどうかに関係なく) は通常、次の 2 つのカテゴリのいずれかに分類されます。ステートレスで静的でグローバルな「サービス オブジェクト」(モジュール)と、ステートフルで動的でローカルな「データ オブジェクト」です。
モジュール グラフ (サービス オブジェクトのグラフ) は通常、アプリケーションの起動時に作成されます。これは Spring などのコンテナを使用して実行できますが、オブジェクト コンストラクターにパラメータを渡して手動で実行することもできます。どちらの方法にも長所と短所がありますが、アプリケーションで DI を使用するためにフレームワークは必ずしも必要ではありません。
要件の 1 つは、サービスが依存関係によってパラメータ化されている必要があることです。これが正確に何を意味するかは、特定のシステムで採用されている言語とアプローチによって異なります。通常、これはコンストラクター パラメーターの形式をとりますが、セッターを使用することもできます。これは、サービスの依存関係が (サービス メソッドの呼び出し時に) サービスのユーザーから隠蔽されることも意味します。
いつ使用しますか?アプリケーションが十分に大きく、ロジックを個別のモジュールにカプセル化し、モジュール間の依存関係グラフを使用すると、コードの可読性と探索性が向上すると思います。
依存関係の注入 の実装の一種です。制御の反転」という原則に基づいてフレームワークを構築します。
フレームワーク GoF の「デザイン パターン」に記載されているように、開発者にそれを実行させる主要な制御フロー ロジックを実装するクラスがあり、このようにしてフレームワークは制御原理の逆転を実現します。
この IoC 原則は、クラス階層としてではなく技術として実装する方法であり、単なる依存性注入です。
DI これは主に、クラス インスタンスとそのインスタンスへの型参照のマッピングを外部の「エンティティ」に委任することで構成されます。オブジェクト、静的クラス、コンポーネント、フレームワークなど...
クラスのインスタンスは「依存関係"、参照を介した呼び出しコンポーネントとクラス インスタンスの外部バインディングは、"注射".
明らかに、OOP の観点から必要に応じてこの手法をさまざまな方法で実装できます。たとえば、次の例を参照してください。 コンストラクターインジェクション, セッターインジェクション, インターフェースインジェクション.
参照をオブジェクトに照合するタスクの実行をサードパーティに委任することは、いくつかのサービスを必要とするコンポーネントを同じサービス実装から完全に分離したい場合に非常に役立ちます。
このようにして、コンポーネントを設計するときに、使用するオブジェクトやサービスの実装変更を心配することなく、他のオブジェクトと連携するためのインターフェイスを信頼して、コンポーネントのアーキテクチャとその特定のロジックだけに集中できます。また、同じオブジェクトを使用している場合でも、完全に置き換えられます (明らかにインターフェイスを考慮して)。