🪬

単体テストだけでないDI(依存性注入)のメリット

2024/09/23に公開

はじめに

DI(Dependency Injection)依存性注入という言葉を知ったのは単体テストについて学習しているときでした。DIは単体テストをするときのためにあるというふわっとした認識だった。

https://gihyo.jp/book/2022/978-4-297-13234-7

ちょうぜつソフトウェア設計入門――PHPで理解するオブジェクト指向の活用を読んで
第7章の依存性注入に「単体テストがアーキテクチャへの気づきの手段としてとても有効だが、DIを単に単体テストのためにやることいった目的観では視野狭窄」といったことが書かれていたので、DIの他のメリットについて調べてみました。

サンプルコード(Swiftで)を例にDIを使った場合と使わない場合の比較をします。

DIを使わない場合のコード

class NetworkService {
    func fetchData() {
        // ネットワークからデータを取得する処理
    }
}

class DataManager {
    private let networkService = NetworkService()
    
    func loadData() {
        networkService.fetchData()
    }
}

DataManagerはNetworkServiceに強く依存しています。もしネットワーク以外からデータを取得したい場合や、NetworkServiceの実装を変更したい場合、DataManagerも変更が必要になります。

DIを用いた設計に変更したコード

protocol DataService {
    func fetchData()
}

class NetworkService: DataService {
    func fetchData() {
        // ネットワークからデータを取得する処理
    }
}

class LocalService: DataService {
    func fetchData() {
        // ローカルデータからデータを取得する処理
    }
}

class DataManager {
    private let dataService: DataService
    
    init(dataService: DataService) {
        self.dataService = dataService
    }
    
    func loadData() {
        dataService.fetchData()
    }
}

DataManagerはDataServiceプロトコルに依存しており、具体的な実装(NetworkServiceやLocalService)には依存していません。これにより、以下のメリットがあります。

  • 依存先を簡単に差し替えられるため、状況に応じて異なるデータソースを利用可能。
  • DataManagerは汎用的なクラスとなり、他のプロジェクトでも再利用可能。
  • 依存関係が明確であるため、変更の影響範囲が限定され、不具合等の原因を特定しやすくなる。

DIとは、クラス間の依存関係なくすこと、簡単に言えば「必要なものを外から渡す」という考え方です。これによって、コードがより柔軟で、そして将来の変更にも強くなり単体テストの容易性にも繋がります。

参考

https://www.avanderlee.com/swift/dependency-injection/

https://qiita.com/eKushida/items/78a58559aedd851d105c

Discussion