DIとは?
外部からインスタンスを注入すること。例:Oracleインスタンス(依存性)を外からコンストラクタにて与えてあげる実装をする。
イメージ的に
昔のスーパーファミコン(上位モジュール)とカセット(下位モジュール)の関係。スーパーファミコンは「カセットの種類」に依存しない作りになっている。
依存関係逆転の原則との関係
SOLID原則である「依存関係逆転の原則」の実現に貢献してくれます。上位モジュールをインスタンス化する際に、下位モジュールをコンストラクタとして渡すのでDIを実装することで依存関係逆転の原則を満たすことにも繋がります。(例えば、上位の画面クラスをインスタンス化する際に、下位のOracleクラスを渡すなど)
メリット
上位モジュールが下位モジュールに依存しない
上位モジュール(クライアントコードや画面など)は下位モジュール(Oracle呼び出しクラスなど)に依存しない作りになる。
自動テストがしやすくなる。
同じインターフェースを継承したMockクラスを作って引数に渡してあげます。それでテストコードを記述するようにします。(例えば、Oracleのテストコードを書く場合にいちいちテストコードを実行する際にもOracleが立ち上がっていたりしないといけないですしそもそもDBに登録されたデータしか返してくれないので、Mockで固定の値を返してくれるクラスを作成し、注入して渡すことでテストをしやすくなります。)
ORマッパーの場合
そもそも、昨今のORマッパーではDBにアクセスをしてデータを取得するというお決まりのソースコードは書かないようになっています。XXモデルなどがあってそこからデータ取得用のメソッドを実行することで取得するなどの機能があったりします。
なので、ORマッパーを使った開発をしている場合はこの限りではない気がします。(単純にテストデータををエンティティオブジェクトに渡してテストするという形になり自然と依存性が注入された状態になる感じです。)
実装
DBの問い合わせ処理を実装を呼ぶ場合
コンストラクタにFactoriesクラスを使って今回使うテクノロジーのクラス(Oracleクラスなど)を与えます。
ViewModelへの実装の注入を行う場合
画面はView(デザインより)とViewModel(ロジックより)に分けて実装するのが良いとされています。
DIコンテナ
注入される側のクラスと注入する側のクラスの対応リストを保持しておきます。
事前に依存するオブジェクトを登録しておいて使う時になったらDIコンテナ経由でオブジェクトを取得します。この設定などはスタートアップスクリプトなどに登録しておくようにします。
DIコンテナの注意点
DIコンテナを使ったからといって依存関係が逆転するわけではない。あくまでクラスが依存しているオブジェクトを外部から注入できる点が良い。
ファクトリークラスとの違いや利点
ソースコードからファクトリークラスを消すことができます。ファクトリークラスだとソース自体を修正しないといけないですが、DIコンテナを使う場合はアプリ外部の設定ファイルから設定することが可能なのが利点になります。
ファクトリークラスと同じように使うケースもある。
DIコンテナクラス(DIなどと命名)を作って、対応する実装クラスを返すようにします。その際はstaticで作ることが多いです。
DIライブラリ(DIフレームワーク)
- Spring Framework(Java)
- google/guice(Java)
- AngularJS(JavaScript:DIフレームワークを内包している)
- InversifyJS(TypeScript)
- TSyringe(TypeScript)
-
Microsoft.Extensions.DependencyInjection(C#)
ライブラリなしでDIコンテナを実装する場合の問題点
- decoratorを使用する必要がある。
- reflectionによるメタデータの取得が必要になる。
- TypeScript(を使ってる場合は)のインターフェース情報がコンパイル時に削除されてしまい、DIコンテナの対応づけができない。
この記事へのコメントはありません。