依存性注入
Dependency Injection (DI)
訳語がおかしいという意見あり。
普通にモジュールを作っていると、ほとんど無意識に別のモジュールを直接指定して呼び出しをしてしまうことがある。
これはモジュール間に強結合を与えてしまい、他のモジュールと差し替えるのが困難になってしまう。
再利用性の低下
実装方法の固定化
テストでのスタブ・ドライバとの結合の困難性
モジュールの循環参照
モジュールAがモジュールBを参照、モジュールBがモジュールCを参照、モジュールCがモジュールAを参照、というような場合、このままでは依存関係の解決ができない。
これを避けるために、どのモジュールと結合すべきかを外部から与える方法が依存性注入である。
依存性注入は当然モジュール外で行われる必要がある。
モジュールの利用者側が、利用する時に必要な依存性を注入するのが基本。
短所
どのモジュールが結合するのか予測不可能になるため、インライン展開のようなことができなくなる。
依存性注入をすることが自己目的化して、無意味に複雑化する。
再利用性がそもそもない。
テストをするにも適切なスタブ・ドライバが作られるわけでもない。
依存性注入のための設定ファイルが複雑化する。
実装方法
以下の2つの方式が考えられる。
設定ファイル方式
プログラム方式
なぜ設定ファイル方式か?
プログラムとは独立して設定できるので、プログラムを変更する必要がない。
テストなどで一時的に一部分だけを差し替えるのが簡単。
なぜプログラム方式か?
設定ファイルが不要。
設定ファイルの読み込みはパフォーマンスを落としやすい。
設定ファイルは固定的だがプログラムであれば条件などを付け加えられる。
設定ファイルはプログラミング言語とは異なる文法を持つが、プログラム方式ならば、プログラムと同じ文法で済む。
直感的に使うことができる。
依存性注入の基本的方法は、ほぼ Proxy パターンとなる。
基本的には、名前からファクトリを呼び出すマネージャを介することになる。
あらかじめ、依存関係を別途定義しておく。
別のモジュールを必要としているモジュールに対して、実際に何を渡すかを決める。
何らかの抽象名をキーにしてインターフェースを値とした Map になるはず。
モジュール名(あるいはインスタンス名) + インターフェース名が妥当なところか。
キーはインターフェース名ではない。インターフェース名にすると、複数のモジュールに対して別のモジュールを渡したいときに渡せなくなってしまう。
初期化段階、あるいは他モジュールを参照した段階で、依存性注入を行うモジュールを呼び出して依存関係を確定する。
共通インターフェースを渡すのが妥当。
依存関係の定義をどうするか?
あらかじめクラス定義を読んでおかなければならないとすると、その時に依存関係が必要とされてしまうことがある。
クラスで一つだけの依存性注入をすればいいケースと、インスタンスごとに依存性注入をすべきケースとがある。
元々外部からオブジェクトを与えられるのであれば、依存性注入をする必要性はない。
具体的な依存性注入の方法
Java
Java のクラスをそのまま使う方法であれば、インターフェースを使うのが正攻法になる。
インターフェースを定義する。
差し替えるオブジェクトはこのインターフェースを実装していなければならない。
依存性注入を行うファクトリを用意する。
ファクトリは自分がどのクラスから呼び出されたのかを取得して、外部定義から実際にインスタンス化すべきクラスを取得する。
ファクトリ自体が依存性を持つのでは意味がないことに注意。
code:java
class YourClass {
static s_factory = new MyFactory(YourClass.class);
IMyObject createInstance() {
IMyObject obj = s_factory.createInstance();
obj.set(...);
return obj;
}
JavaScript
少なくとも、モジュールの呼び出し時点では依存性注入はできない。(元々そういう設計思想になっていない)
以下はモジュールを直接呼び出してしまう。
ES2015 以降の import
Node.js など CommonJS の require
このため、モジュールの初期化時点で依存性を注入する必要がある。
関連
N層システムにおける、スコープ違いのファクトリの外出し方法