Dependency Injection(依存の注入)とは?

  • 2
    Like
  • 0
    Comment

自分が理解したいためにまとめました!間違いがあればぜひご指摘を。

1. Dependency Injection(DI)ってなんやねん。

意味を訳すと、Dependency = 依存で、
Injection = 注入、注射するです。つまり、
「依存していた部分を、外から注入すること」by @hshimoさん
つまり、外からオブジェクトを受け取ることです。

2. DIが便利なわけ / 使いたい理由

*@hshimo さんから言葉をお借りしてます

柔軟性がない -> 決め打ちなので、柔軟性がなくカスタマイズしにくい
テストしにくい -> 外から動的に動作を変更できないので、テストしづらい

ってイキナリ言われても解らんですよね。そこで例えを使って考えましょう。

3. 例で考える

たとえば、ドクターがいて、ナースさんがいて、カルテがあったら、普通はドクターがカルテを書いて、ナースさんに渡してそれを元に患者に対応しますよね?(DI後)

でも、医者がカルテ作成を全部ナースさんに丸投げしたらどうなるでしょうか? ナースさんはいちいちドクターに患者の状況を聞いてから、カルテを作成しないと行けないです。(DI前)

こうなると、ナースさんの仕事が増えてすごく大変。しかもカルテをドクターが直したい、となったら、ナースさんはまたイチから作成しないといけません。(DI前)

Screen Shot 2017-07-16 at 12.06.57 AM.png

DI前のコード

protocol Carte {}
class Doctor{
   func assignNurse() -> Nurse{
      var nurse = Nurse()
      return nurse
   }
}

class Nurse {
    let carte: Carte

    init() {
        carte = Carte()
    }
}
main.swift
doctor.assignNurse()

DI後コード

class Carte {}
class Doctor{
  func makeCarte() -> Carte{
    var carte = Carte()
    return carte
  }
}

class Nurse {
    let carte: Carte

    init(_ carte: Carte) {
        self.carte = carte
    }
}
main.swift
var carte = doctor.makeCarte()
var nurse = Nurse(carte)

DI後コードでは、NurseクラスはCarteをinit(作成してない!)
そして外からcarteオブジェクトを注入にしているので、carteに変更があった場合、Nurseの中身は書き換えなくてもいいので、テストが楽。

DIがを使いたい理由を簡単に説明すると、
一つのクラスが仕事を持ちすぎているので(クラス依存)、スムーズに事が進むように、仕事を分担する、という感じで。ナースさんはカルテの中身を知りたいけど、作成方法は一切知らなくてもいい、という感じです。

え、例えとコードが微妙??ごめんなさい。

4. 色々なところで使うDependency Injection

1. Viewcontrollerから別なViewControllerを開くとき

ViewController.swift
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if getIndexPathForSelectedCell() != nil {
            let newView = segue.destination as! DetailViewController
            newView.folderIndexPath = getIndexPathForSelectedCell()!
            newView.dataSource = self.dataSource
        }
    }

DetailViewController.swift
    var folderIndexPath:IndexPath
    var dataSource:DataSource

    init(_ folderIndexPath:IndexPath, _ dataSource:DataSource) {
        self.folderIndexPath = folderIndexPath
        self.dataSource = dataSource
    }

ここでは、DataSourceクラスはViewControllerのどこかでinitializeされていて、それをDetailViewControllerにパスしています。

2. AppDelegateにあらかじめDIしておく

AppDelegate.swift
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.


        //dependency injection
        let rootViewController = window?.rootViewController as! UINavigationController
        let photoViewController = rootViewController.topViewController as! PhotoViewController

        photoViewController.store = PhotoStore()

        return true
    }
PhotoViewController.swift
    var store:PhotoStore!

ここではAppDelegateから画面に現れる最初のViewControllerを指定してます。いつもはViewControllerが勝手に出ますが、ここではPhotoViewControllerを出すように指示しています。
そのほかに、PhotoStoreクラスのinitializeもここで行なって、PhotoViewControllerにDI(注入)しています。

参考!
猿でも分かる! Dependency Injection: 依存性の注入
Dependency Injection in Swift