ちょっと前にDIが流行ったみたいなのでコードを書かずに考え方を説明してみる

# この記事の対象者
- DIって名前は知っているけど具体的に何なのか分からない
- DI関連の記事を見たけど何が書いてるのかよく分からない

# 初めに
ちゃんと説明するには私の知識と語彙力が足りないので、ふわっとざっくり理解できる記事を目指します。
厳密には異なっている内容もあると思いますが許して……


# DI(Dependency Injection)とは
## Wikipedia(2020/7/25現在)によると
>DIを利用したプログラムを作成する場合、コンポーネント間の関係はインタフェースを用いて記述し、具体的なコンポーネントを指定しない。
>具体的にどのコンポーネントを利用するかは別のコンポーネントや外部ファイル等を利用することで、コンポーネント間の依存関係を薄くすることができる。
>> https://ja.wikipedia.org/wiki/%E4%BE%9D%E5%AD%98%E6%80%A7%E3%81%AE%E6%B3%A8%E5%85%A5

こんな感じ。さっぱり分からない。

## ざっくり言うと
使用するクラスの「実体」を使うのではなく、最低限の機能を持った「インターフェース」を使うようにコードを書いておいて、使用する側が後からクラスを定義して引き渡せるようにする設計パターンの事です。

これでも普通に分かりづらいですね。

# DIってこういうこと
今回はコードを書かない縛りなので、実世界の話で考えてみましょう。
例えばそうですね。
一般的な「朝起きてから会社に出勤するまで」の流れを手順書にしてみます。

```
//朝のルーチンワーク
起きる

顔を洗う

うがいをする

朝食を食べる

歯を磨く

着替える

出社
```
こんな感じでしょうか。歯磨きとか着替えのタイミングは人それぞれかもしれませんが、そこは今回の記事では触れません。
重要なのは「朝食を食べる」の部分です。

皆さんの今日の朝食は何でしたか。
白いご飯、食パン、お粥、お餅、うどん、サンドイッチ……色々あると思います。
もしもここで「朝食」ではなく、「白いご飯を食べる」「食パンを食べる」としたらどうなるでしょうか。
メニューを限定してしまうと、毎朝そのメニューしか食べられないことになります。
炊飯器が壊れたり、食パンを買いそびれたりしていたら朝食を摂れない、なんてことになるわけです。

ですがこの手順では「朝食」としています。
こうなっていることで、毎朝微妙に異なるであろう状況に対して、「朝食であれば何でもいい」という柔軟性を持たせることができるわけです。

このように、手順をまとめる時点では具体的な物体ではなく要件だけを決めておいて、後から要件を満たす物体を使って処理を行うような設計パターンや手法の事をDIと言います。

# 実際後から実装を決める形で問題ないのか
よくDI関連の記事に書いてある内容で、「後から注入される物体(ここで言う「ご飯」「パン」)のテストはしなくていい」という記述があります。
この辺りの記述は曖昧だったり、一部端折って書かれてたりするので、筆者の意図と読み手の解釈が一致しないことが多々あると思ってます。
私の解釈をお話しすると、

1. 後から注入される物体**が求められる機能を果たしているかどうか**のテストは**クラス作成者がやるので、それを使う側は**テストしなくていい
2. 実際に後から注入してちゃんと動作するかどうかは**テストしなければならない**

と思ってます。

## 1. 後から注入するオブジェクトのテストについて
これはDIに限らず、オブジェクト指向的な考え方の中では普通だと思いますが、クラスが必要な機能を満たしているかどうかはクラスを作った人がテストするべきです。
実際世の中にあるライブラリの多くはライブラリ作成者がテストを実行したうえで公開して下さってます。
だからこそ我々利用者側は、特にそのライブラリの機能をテストせずに使えるわけです。ありがたや。

DIの場合は、実装すべきインターフェースや基底クラスが決まっているため、それらをきちんと実装すれば理論上「機能を満たしている」状態にはなりますが、それを動かしたときにちゃんと動くかどうかまでテストしましょう。
実行時エラーは思わぬところに潜んでいます。

今回の例で言えば、「ご飯」「パン」が、「朝食」としての機能を果たしているかどうかのテストは、朝のルーチンワークを実行する「あなた」がテストするのではなく、そのメニューを考えた人がテストを行うべき、ということです。
朝ごはん作るときにいちいち「栄養素がどうの」とか「カロリーがどうの」って言うのを自分で計算しないですよね？
大抵の場合、レシピ通りに作ってレシピ通りの栄養を摂取できているはずです。

## 2. 後から注入される側のテストについて
では後から注入されたクラスを使用する側のテストはどうでしょうか。
開発段階でのテストはもちろん必要でしょう。注入されるであろうオブジェクトのダミーを作って、正しい値が渡っているか確認するくらいのテストは必要です。

ではちゃんとしたクラスが出来上がった後は？ 1のテストをクリアしているオブジェクトなら、テストしなくても問題ないか？
私の考えでは「**否**」です。ここはちゃんとテストするべきでしょう。
1でも書きましたが、実行時エラーは思わぬところに潜んでいます。

ただし、実行してエラーになった場合は、**注入する側のクラスを修正する**ようにします。
注入される側の処理を直してしまうと、その修正は**今回のクラスで正しく動作するように修正した**ことになってしまうので、**別のクラスを注入したときに上手くいかなくなる**ことがあります。

今回の例で言えば、例えば朝食に**超大盛アツアツ激辛カレー**を用意したとします。
（実際こいつが朝食にふさわしいかは諸説ありますが、朝カレーはスパイスによって代謝が良くなるとか言いますし、朝から大盛ご飯食べる人もいるので当記事においては許容します）

朝食として**超大盛アツアツ激辛カレー**を「注入」して朝のルーチンを実行したとします。
処理自体に矛盾はありませんので、ちゃんと手順を実行できますが、**私は朝がっつり食べられないし、辛いの滅茶苦茶苦手なので、多分食べたら一日具合悪くてとても仕事どころじゃありません。**これが実行時エラーです。

# DIコンテナってのは何なのか
こちらもたまにDI関連の記事に出てくる単語です。
一番最初にちらっと触れた通り、DIというのは、**後からクラスの実体を決めて処理を行うようにすることで、クラス間の結合度を弱める設計パターンやプログラミング手法の事**です。
なので、**具体的にどういう手段でクラスを注入するかっていう方法までは定義に含まれていない**のです。

もちろん、使用環境が変わる都度、参照設定を直して別のライブラリからクラスを引っ張り出して注入する方法でもいいし、
パスを決めておいてそこにdllを配置し、動的にアセンブリを読み込んでクラスを作って使うでもいいし、
ユーザーが使うdllをダイアログとかで決めて、やはり動的読込を行うでもいいわけです。

で。
DIする場所が少なければ、その都度クラス定義を読み込んでインスタンス作って引き渡す処理を記述してあげれば問題ありませんが、箇所が多くなると段々面倒になってきます。
**その辺りをうまい事ラップして、良い感じにクラスを作ってくれる処理をまとめたライブラリが「DIコンテナ」と呼ばれます。**
ものすごくふわっとした説明になってますが、「DIコンテナ」とはそういうものです。
具体的にどういう方法でラップしていて、実際にインスタンス作るのはどういう方法でやってるのかとかは、それぞれのライブラリ開発者がどう実装しているかによるのでわかりません。

察しの良い方ならわかると思いますが、DIを使うシステムを設計する際に、「DIコンテナを使う」って書いておけば、世の中にあるDIコンテナの中から使いやすいライブラリを吟味して使うことができます。
ライブラリ名を指定して設計してしまうと、そのライブラリが今回の実装に合わないものであっても使わざるを得なくなってしまいます。
これがDIです。

//でもプログラマー側としては、設計時にはちゃんとどのライブラリ使うか明示してもらった方がいい場合もあったりします。

# まとめ
//まとめるほどの記事じゃないけど一応ね。

- DIとは必要な機能を定義したインターフェースで処理をするように設計して、後から必要なクラスを定義して使えるようにする設計パターンやプログラミング手法の事
- やっぱりテストはやったほうがいい
- DIをどう実現するかは自由

以上です。