初心者による妄想DI（モノマネ芸人の巻）

## DIとは
引用元：[weblio辞書](https://www.weblio.jp/content/Di?edc=BINIT)
フルスペル：Dependency Injection
読み方：ディーアイ
別名：**依存性**注入，**依存性**の注入

DIとは、プログラミングにおけるデザインパターン（設計思想）の一種で、オブジェクトを成立させるために必要となるコードを実行時に注入（Inject）してゆくという概念のことである。

---

## 依存性の注入？？？
※依存性と言う翻訳は間違いのようです。
[DI != 依存性の注入](https://little-hands.hatenablog.com/entry/2018/05/27/dependency-injection)
@loveeさんありがとうございます！

---

**依存**という言葉が私には抽象的で理解しずらいので
まず**依存**という概念から理解するために
私なりの具体例を考えてみました。


---

## ***「モノマネ芸人」*** と ***「歌手」***

---

「**モノマネ芸人**」はマネるため常に「**歌手**」を監視しています。
そして「**歌手**」が歌詞を変えると自分が歌う歌詞も変わります。

一方「**歌手**」は「**モノマネ芸人**」に合わせる必要はないので
「**モノマネ芸人**」の歌詞が変わろうと「**歌手**」の歌詞はかわりません。

この状況を
「**モノマネ芸人**」は「**歌手**」に依存しているといえます。

---

### 具体例をコードにしてみます。
歌手クラスを作ります。

```swift
class 歌手クラス {
    let 名前: String = "和田ア〇子"
    let 歌詞: String = "あの頃は～♪"
    func 歌う() {
        print("「\(歌詞)」")
    }
}
let 歌手 = 歌手クラス()
歌手.歌う()
//「あの頃は～♪」
```
---

続いてモノマネ芸人クラスを作ります。

```swift
class モノマネ芸人クラス {
    let 名前: String = "シャチ〇コ"
    let 本人: 歌手クラス = 歌手クラス() // もはや和田ア〇子
    func マネる() {
        本人.歌う()
        print("by \(名前)")
    }
}
let モノマネ芸人 = モノマネ芸人クラス()
モノマネ芸人.マネる()
// 「あの頃は～♪」
// by シャチ〇コ
```
これで**シャチ〇コ**が**和田ア〇子**に依存している状態を
作ることができました。

---
この状態だと
インスタンス化すると自動的に
**歌手** = **和田ア〇子**
**モノマネ芸人** = **シャチ〇コ**
が確定してしまうし
モノマネ芸人は**和田ア〇子**のマネしかできません。

---

### **「シャチ〇コ」** が **「ミス〇ル」**を歌ったりするにはどうすればいいだろうか。。。

---

まず歌手クラスはイニシャライザで名前と歌詞を決められるようにしましょう。

```swift
class 歌手クラス {
    let 名前: String 
    let 歌詞: String
    init(名前: String, 歌詞: String) {
        self.名前 = 名前
        self.歌詞 = 歌詞
    }
    func 歌う() {
        print("「\(歌詞)」")
    }
}
let ミス〇ル = 歌手クラス(名前: "ミス〇ル", 歌詞: "シーソーゲーム♪")
ミス〇ル.歌う()

// 「シーソーゲーム♪」
```
これで和田ア〇子以外の歌手を作れるようになりました。

---

モノマネ芸人もイニシャライザで名前と本人（歌手）を決められるようにしましょう。

```swift
class モノマネ芸人クラス {
    let 名前: String 
    let 本人: 歌手クラス
    init(名前: String, 本人: 歌手クラス) {
        self.名前 = 名前
        self.本人 = 本人
    }
    func マネる() {
        本人.歌う()
        print("by \(名前)")
    }
}
let モノマネ芸人 = モノマネ芸人クラス(名前: "シャチ〇コ", 本人: ミス〇ル)
モノマネ芸人.マネる()

// 「シーソーゲーム♪」
// by シャチ〇コ
```

これでめでたくミス〇ルを歌うことができました。

---

### 実は今のがDIの内の一つなのです。
**Initializer Injection**
そのままですがイニシャライズ時に依存先のオブジェクトを注入することです。

今回だとこの部分です。

```swift
    init(名前: String, 本人: 歌手クラス) 
```
イニシャライズで依存先の歌手を代入してますね。

---

ではもう一つのDIである
**Method Injection**という方法があります
こちらはどんなものかと言うとその名の通り
メソッドを呼ぶ時に依存先のオブジェクトを注入することです。
次はメソッドインジェクションを使った
モノマネ芸人のコードを見てみましょう。

---

```swift
class モノマネ芸人クラス {
    let 名前: String 
    init(名前: String) {
        self.名前 = 名前
    }
    func マネる(本人: 歌手クラス) {
        本人.歌う()
        print("by \(名前)")
    }
}
let モノマネ芸人 = モノマネ芸人クラス(名前: "シャチ〇コ")
モノマネ芸人.マネる(本人: ミス〇ル)

// 「シーソーゲーム♪」
// by シャチ〇コ
```
「マネる」メソッドの引数で依存先である歌手インスタンスを代入しています。
こちらもめでたくミス〇ルを歌うことができました。

---

# しかし！

---

実はまだ違和感が残っています。

そもそもの話なのですが
モノマネ芸人って、歌だけじゃないですよね？


----

モノマネといえば
歌手や俳優やスポーツ選手や芸人など、、、
なんなら動物のまねだってします.

要するに
**芸**を**披露**する人をマネるのです！

いわば**「芸能人」**に依存すればいいのです！

※動物は人じゃないですね、、、

---

「何かしら**芸**を持ち**披露**する人を**芸能人**とする」

このように
**「共通な属性を抜き出し、これを一般的な概念として捉えること」**
を**抽象化**といいます。
SOLID原則というオブジェクト指向の設計原則によると

 - 上位レベルのモジュールは下位レベルのモジュールに依存すべきではない。両方とも**抽象**（abstractions)に依存すべきである。
 - **抽象**は詳細に依存してはならない。詳細が**抽象**に依存すべきである。

つまり歌手もモノマネ芸人も芸能人という**抽象**に依存するように設計するとよさそうです。

---

## プロトコル
Swiftでは**プロトコル**を使うことで**抽象**を表現できます。

```swift
protocol 芸能人 {
    var 名前: String { get }
    var 芸: String { get }
    func 披露() 
}
```

**名前**と**芸**を持っていて
**披露**することができるやつ

すなわち**芸能人プロトコル**です。

---
このプロトコルで歌手を表現してみます。

```swift
class 歌手クラス: 芸能人 {
    let 名前: String
    let 芸: String
    init(名前: String, 芸: String){
        self.名前 = 名前
        self.芸 = 芸
    }
    func 披露() {
        print("「\(芸)」")
    }
}
let 小田〇正 = 歌手クラス(名前: "小田〇正", 芸: "言葉にできない〜♪")
小田〇正.披露()
// 「言葉にできない〜♪」

```
---

試しにポケ〇ンクラスも作ってみます。

```swift
class ポケ〇ンクラス: 芸能人 {
    let 名前: String
    let 芸: String
    init(名前: String, 芸: String){
        self.名前 = 名前
        self.芸 = 芸
    }
    func 披露() {
        print("サ〇シ「いけ！\(名前)！」")
        print("「\(芸)」")
    }
}
let ピ〇チュウ = ポケ〇ンクラス(名前: "ピ〇チュウ", 芸: "ピッカーーー！")
ピ〇チュウ.披露()
// サ〇シ「いけ！ピ〇チュウ！」
// 「ピッカーーー！」

```

---

モノマネ芸人側はこうなります。

```swift
class モノマネ芸人クラス {
    let 名前: String 
    let 本人: 芸能人
    init(名前: String, 本人: 芸能人) {
        self.名前 = 名前
        self.本人 = 本人
    }
    func マネる() {
        本人.披露()
        print("by \(名前)")
    }
}
let モノマネ芸人 = モノマネ芸人クラス(名前: "シャチ〇コ", 本人: ピ〇チュウ)
モノマネ芸人.マネる()

// サ〇シ「いけ！ピ〇チュウ！」
// 「ピッカーーー！」
// by シャチ〇コ
```

---

ずいぶん自由がきく設計となり
モノマネ芸人に様々な
芸能人をDIできるようになりました。

しかしもうちょっとお付き合いいただきたい。

**芸能人**の**芸**が**String型**しか扱えないのも
修正したいです。

```swift
protocol 芸能人 {
    var 名前: String { get }
    var 芸: String { get } // これです。
    func 披露() 
}
```

---

Swiftでは動的に型を指定する仕組みとして
ジェネリクスと言う仕組みを使います。
protocolに使う場合は

```swift
assosiatedtype 型変数
```
で型変数を定義しておいて
class内にて

```swift
typealias 型変数 = 指定の型
```

としてクラス側で型を決めることができます。

---

今回の場合だと

```swift
protocol 芸能人 {
    associatedtype 芸種 // この型変数が↓
    var 名前: String { get }
    var 芸: 芸種 { get } // ここにも反映されます
    func 披露() 
}
```

※型変数と言う言葉は正しいかどうかわかりませんがわかりやすいので今回はそう呼ばせてください！


---

[String]を持った歌手クラス

```swift
class 歌手クラス: 芸能人 {
    typealias 芸種 = [String] //←ここでprotocolの芸種という型変数に[String]型を代入
    var 名前: String
    var 芸: 芸種 // ここも
    init(名前: String, 芸: 芸種){ //ここも
        self.名前 = 名前
        self.芸 = 芸
    }
    func 披露() {
        for g in 芸 { // 配列で定義しているのでイテレータ回せちゃいます
            print("「\(g)」")
        }
        
    }
}
let 小田〇正 = 歌手クラス(名前: "小田〇正", 芸: ["ら〜らーらー","ららーらー","言葉ぁに","できなぁい"])
小田〇正.披露()
// 「ら〜らーらー」
// 「ららーらー」
// 「言葉ぁに」
// 「できなぁい」
```


---

Int型の芸を持ったスクワットクラス

```swift
class スクワットクラス: 芸能人 {
    typealias 芸種 = Int
    let 名前: String
    let 芸: 芸種
    init(名前: String, 芸: 芸種){
        self.名前 = 名前
        self.芸 = 芸
    }
    func 披露() {
        print("レッツスクワット！")
        for i in 1...芸 {
            print("\(String(i))!")
        }
        print("ビクトリー！")
    }
}
let ビリー = スクワットクラス(名前: "ビリー", 芸: 5)
ビリー.披露()

// レッツスクワット！
// 1!
// 2!
// 3!
// 4!
// 5!
// ビクトリー！

```

---

しかしここで注意です。
associatedtypeを持ったprotocolはプロパティの型としてとして利用できません

```swift
class モノマネ芸人クラス {
    let 名前: String
    let 本人: 芸能人 // ここです。
    ...
```
こんなエラーが出ます。

```
error: protocol '芸能人' can only be used as a generic constraint 
because it has Self or associated type requirements
```
google翻訳によると
**「プロトコル '芸能人'は、自己または関連する型の要件があるため、ジェネリック制約としてのみ使用できます。」**

つまり型として使うには**typealias**などで型を指定しないと制約として機能しないので
ストアドプロパティには直接定義できなくなるのです。
ただし**ジェネリック制約としてのみ使用できる**ようです。

---

エラーメッセージに言われた通り
ひと工夫してジェネリクスを使って動的な型（クラス）を指定すると実現が可能です。


```swift
class モノマネ芸人クラス<T: 芸能人>{
    let 名前: String 
    let 本人: T
    init(名前: String, 本人: T) {
        self.名前 = 名前
        self.本人 = 本人
    }
    func 披露() {
        本人.披露()
        print("by \(名前)")
    }
}
```

---

そうすると
小田〇正をDIしたり

```swift
let モノマネ芸人 = モノマネ芸人クラス(名前: "シャチ〇コ", 本人: 小田〇正)
モノマネ芸人.披露()
// 「ら〜らーらー」
// 「ららーらー」
// 「言葉ぁに」
// 「できなぁい」
// by シャチ〇コ

```
---

ビリーをDIしたり
も出来ちゃうのです。

```swift
let モノマネ芸人 = モノマネ芸人クラス(名前: "シャチ〇コ", 本人: ビリー)
モノマネ芸人.披露()
// レッツスクワット！
// 1!
// 2!
// 3!
// 4!
// 5!
// ビクトリー！
// by シャチ〇コ

```

---

## 最後に
DIは手段であり目的ではないのですが
設計に関するドキュメントを読むとよく出てきて
さらっと流されることもしばしばあります。DIに限りませんがｗ
私はそこから先に進めないことが多々あったので
もし同じ状態の方がいらっしゃれば
一旦DIのイメージが沸いた状態で
ドキュメントに戻るとより理解が深まるのではないかという想いで
敢えてDIという手段自体に焦点を当てた記事にしました。

有識者からすると突っ込みどころは満載だろうと予測しております。
可能であれば間違いやご指摘をたくさんいただけると幸いです。

以上！！
---