Swiftで理解する依存性の注入(DI)

# はじめに
DIについて自分なりに勉強したとを、当時何も理解できなかった自分に向けて説明するつもりで書いていこうと思います。

「僕はこう解釈しています」というところがたくさん出てくるので、「それは解釈としておかしくね？」というところがあったら教えてください🙇

それにしても、``Dependency Injection``っていう響きがかっこいいですね。
**邪王炎殺黒龍波**並みに厨二心をくすぐられます。必殺技感がたまんないですね。はい。

# この辺がわかっていると理解しやすいかも！
- protocolの使い方がある程度理解してますという人

# こんな人に読んでほしい
- DIってなんか聞いたことあるけど、よくわからんねんって人

# 結論から話しましょう
雑に言うとDIってこんな感じだと認識しています。
>クラスA内でクラスBを使う。このときに、クラスA内でクラスBのインスタンスを生成するのではなく、クラスAの外で生成されたクラスBのインスタンスをクラスAが受け取るようにすること。

と。言われましても...ってなると思うので実際にコードを使ってみましょう。

### DIを使わない場合
- ポイントはクラスAの中でクラスBを使うとき、インスタンスの生成は**クラスA**が行っている
```
final class A {
    private let b = B()
    func greeting() {
        b.sayHello()
    }
}

final class B {
    func sayHello() {
        print("hello")
    }
}

let a = A()
a.greeting()  // hello
```

### DIを使った場合

- ポイントはクラスAの中でクラスBを使うときに**クラスA内でBのインスタンス生成を行っていない**ということ

```
class A {
    private let b: B

    init(b: B) {
        self.b = b
    }

    func greeting() {
        b.sayHello()
    }
}

class B {
    func sayHello() {
        print("hello")
    }
}

let a = A(b: B())
a.greeting()
```

### つまりどういうこと？
つまり、DIというのは使用するクラスのインスタンス生成を自らは行わない。
誰かにインスタンスを生成してもらって**外から注入してもらうこと**ということです。

誰かが``インスタンス(依存性)``を生成しそのインスタンスを``注入``してくれるので**依存性の注入**というんですね。

# DIを使うことのメリットをイメージでつかむ
初めにDIを使うとどんな良いことがあるのか？ということから説明をしていきます。

何か難しいことを理解するときはイメージが大切だと思うので、まずは僕の中のイメージを共有します。
これは**DI**自体のイメージではなく、DIによって得られるメリットの１つのイメージです！

では、下のキャラクターのイラストをご覧ください。

|フック船長|クロコダイル|
|:--:|:--:|
|![189281.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2644705/335fc853-0c6a-9332-f911-e318dea635ac.png)|![クロコダイル.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2644705/c8493cc3-bdfe-7fe4-319e-1fcadda45e22.png)|

フック船長とクロコダイルですね。２人の共通点はなんでしょう？

```左手の鉤爪```ですね。

２人とも同じ鉤爪を持っていますが、この２人の鉤爪は少し違います。

何が違うかというと、クロコダイルの鉤爪は``付け替えが可能``です。鉤爪のカバーみたいものを外すと、毒針のようにもなるし、鉤爪が折れてしまえば、中からナイフもだせます。しかし、フック船長の鉤爪は``ワンパターン``です。たぶん。

クロコダイルは状況や相手に合わせて、使う武器を選べますが、フック船長はそんなことはできません。

これがDIとどんな関係があるのか？
DIを使うとクロコダイルのように状況に応じて使うものを変更することができます。

DIを使わないでコードを書くのが``フック船長``、DIを使ってコードを書くのが``クロコダイル``といったイメージです。

DIを使うことで得られるメリットは**都合の良いように何かを変更すること**が可能になるということです。

では、実際にDIを用いて**都合のいいように変更する**という例をコードを使って紹介していきます。

# 計算するサンプルを用いてDIのメリットを紹介！
## DIを使わないパターン
### Model
- 足し算をするだけのModelクラスです
- サーバーと通信している想定です(非同期にできてないじゃないかというのは無視してください...)
- サーバーでは足し算の結果を戻すとともにログをデータベースに保存しているみたいなこともしているとします
```
final class AdditionModel {

    func calculate(num1: Int, num2: Int) -> Int {
        // サーバーと通信して結果を取得する。
        // サーバーではデータベースの何かしらの書き換えも行っている
        num1 + num2
    }
}
```
### Modelを呼び出すクラス
- Modelクラスの計算メソッドを呼び出します
- DIをしていないのでクラス内でAdditionModelのインスタンスを生成しています
- MVPを使用していればPresenterにあたります
```
final class Hoge {

    private let calculator = AdditionModel()

    func showResult() {
        let result = calculator.calculate(num1: 3, num2: 2)
        print(result)
    }
}
```

### Hogeクラスの実行
- 以下のようにHogeクラスをインスタンス化し、``showResult``を呼ぶと計算結果が出力されます
```
let hoge = Hoge()
hoge.showResult()
```

特に複雑なことはしていないので、ここまでは大丈夫かと思います。

### テストをしたい
では、Hogeクラスのテストをしたいと思ったとしましょう。

ただ、テストのときには``AdditionModel``の``calculateメソッド``は呼びたくありません。
このメソッドを呼ぶとサーバーと通信して、データベースの値を書き換えてしまいます。

Hogeクラスを書き換えずにテストをできるようにするためにはどうしたらいいのでしょうか？

### ここでDIが登場します！

## DIを使う場合
### protocol
- protocolを以下のように作ります
```
protocol Calculator {
    func calculate(num1: Int, num2: Int) -> Int
}
```

### Modelクラス
- Modelクラスは2つ作ります
- １つは本番用、もう１つはテスト用でサーバーとは通信しないクラスです
- DIをしないときと違うのはModelに**protocolを準拠**させているところです
- そのほかは変わりません

```
final class AdditionModel: Calculator {

    func calculate(num1: Int, num2: Int) -> Int {
        // サーバーと通信して結果を取得する。
        // サーバーではデータベースの何かしらの書き換えも行っている
        print("本番だよ")
        return num1 + num2
    }
}

final class AdditionModelMock: Calculator {

    func calculate(num1: Int, num2: Int) -> Int {
        // サーバーと通信しないようにしておく。
        // サーバーから返ってきてほしい値を設定する
        print("モックだよ")
        return num1 + num2
    }
}
```

### Modelを呼び出すクラス
- DIを使って、クラスのインスタンス生成を``Hogeクラス``のなかではしないようにしました
```
final class Hoge {

    private let calculator: Calculator

    init(calculator: Calculator) {
        self.calculator = calculator // ← ココ！　DI!　インスタンスを外部から貰ってる！
    }

    func showResult() {
        let result = calculator.calculate(num1: 3, num2: 2)
        print(result)
    }
}
```

### Hogeクラスの実行
----------
#### 本番用のModelを使う
- 本番用の``AdditionModel``を使いたいときは以下のようにします
- Hogeクラスをインスタンス化するときに、使用したいModelのインスタンスを生成して渡します
- AdditionModelは``Calculator``protocolに準拠しているのでHogeクラスのインスタンス生成時に使用することができます。
```
let hoge = Hoge(calculator: AdditionModel())
hoge.showResult()

// 以下のようにコンソールに表示されます
// 本番だよ
// 5
```
#### テスト用のModelを使う
- テスト用のModelを使いたいときは以下のようにします
- AdditionModelMockも``Calculator``protocolに準拠しているのでHogeクラスのインスタンス生成時に使用することができます。

```
let hoge = Hoge(calculator: AdditionModelMock())
hoge.showResult()

// 以下のようにコンソールに表示されます
// モックだよ
// 5
```

このように、protocolとDIを組み合わせることで``Hogeクラス``の内容を変えることなく、呼び出す処理の内容は変えることができます。

呼び出すクラスが自由に変えられるというのが僕の中ではクロコダイルのイメージでした。

### 補足
今紹介したように、クラスを差し替えられることがDIのメリットであるという紹介をしました。
ただ実際はDIを用いてDIPをするから、このようなメリットが得られると解釈しています。

「え？どゆこと？」ってなった人はとりあえず**DIのメリット=差し替え可能**と理解しておいていいと思っています。

DIPってなんやねんという話をすると長くなるので、ここでは説明を省きますが、詳しく知りたい方は``依存性逆転の法則(DIP)``を調べてみてください。

# まとめ
初めに紹介したように、``DI``とは
**使いたいクラスのインスタンスを自分で生成するのではなく、外から注入してもらうこと**
です。

# おまけ
## 具象を注入することはDIではないのか？
僕の解釈では具象を注入する場合、抽象を注入する場合のどちらもDIになると解釈しています。
ただし、具象を注入することによって得られるメリットはほとんどないのではないか？と思っています。

具象を注入する場合、先に挙げた、クラスを置き換えるということができなくなってしまいますね。

# おわりに
いかがだったでしょうか。少しでもDIとDIを使うことのメリットをイメージできていたら幸いです。
僕の解釈が盛大に間違っている可能性も大いにあるので、ご自身でも、ぜひ調べてみてください！