0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

KotlinによるAndroid開発で学ぶDI入門

Posted at

Kotlin / Android開発で学ぶ Dependency Injection(DI)入門

最近Androidアプリ開発の勉強をしている中で、Hiltを学習しよう!と思ったときにDIの理解が足りなかったため学習に至りました。


目次

  • DI(Dependency Injection)とは何か
  • なぜDIが必要なのか
  • コード例で理解するDI
  • モック/スタブとテストの関係
  • HiltなどDIフレームワークとの関係

DI(依存性注入)とは?

Dependency Injection(依存性注入) とは、「クラスが必要とする依存オブジェクトを、外部から渡してあげる」設計手法です。

例:Car クラスが Engine を必要とする場合、Car クラスが自分で Engine を生成するのではなく、外部から Engine インスタンスを渡すようにします。


// Engineインターフェース(設計図)
interface Engine {
    fun start()
}

// MainEngineクラス(具体的な実装)
class MainEngine : Engine {
    override fun start() {
        println("Engine started")
    }
}

// Carクラス(Engineインターフェースに依存)
class Car(private val engine: Engine) {
    fun drive() {
        engine.start()
    }
}

イメージでいうと、エンジニアAさんとエンジニアBさんがいたとします。
・エンジニアAさん⇒Carクラスを作成
・エンジニアBさん⇒Engineクラスを作成
こうすることで、仮にエンジンインスタンスを変更したいときにはBさんのコードをいじるだけですむ。つまり保守性が上がるということ。

なぜDIが必要なのか

先ほども言ったようにコードの管理がしやすくなるからです。
責任を分散する、依存関係を緩くすることでコードの修正が簡単になったり、明示的に引数として書くので読みやすくなったりします。

コード例で理解するDI

では、実際にDIありとなしのコードを比較してみましょう。

//DIなし
// Engineの実装クラスを作る
class MainEngine : Engine {
    override fun start() {
        println("Engine started")
    }
}

class Car {
    private val engine = MainEngine()  // 実装クラスを直接インスタンス化

    fun drive() {
        engine.start()
    }
}

//DIあり(今回の話)
class Car(private val engine: Engine) {
    fun drive() {
        engine.start()
    }
}

// クラスの外で実装クラスのインスタンスを作成して渡す
val engine = MainEngine()  // ← 実装クラスのインスタンス
val car = Car(engine)      // ← 依存性を注入(DI)

モック/スタブとテストの関係

次にモックスタブについて簡単に説明します。

スタブ:返り値を固定するための仮の実装
    ⇒プログラムの振る舞いさえ分かればいい
モック:呼び出しの有無や回数を検証する目的の実装
    ⇒プログラム実行時の動きまで確認したい

//スタブテスト
class StubEngine : Engine {
    override fun start(): String {
        return "Stub engine started"
    }
}
val stubEngine = StubEngine()
val car = Car(stubEngine)//スタブテスト用エンジンインスタンスを車クラスに渡す

エンジンクラスとは別に、スタブテスト用エンジンクラスも作る。(DI設計)

//モックテスト
class MockEngine : Engine {
    var startCalled = false

    override fun start(): String {
        startCalled = true
        return "Mock engine started"
    }
}
val mockEngine = MockEngine()
val car = Car(mockEngine)
println(mockEngine.startCalled) // true なら、start() が呼ばれた証拠

モックはスタブと違い、中身のメソッドがちゃんと動くかを確認したい。今回はstart()インスタンスがきちんと動くか確認したい。

HiltなどDIフレームワークとの関係

これまで書いたように、DIはとても重要な設計思想である。そのために楽に、自動的にやってくれるのがHiltなどのDIフレームワークである。
Hiltに関してまた、別の記事でまとめようと思います。

感想

DIはとても大事な設計思想であり、アーキテクチャ手法であることがよく分かりました。
ただ、理解度はまだ7〜8割程度で、どこかニュアンスを間違えている可能性もあります。
もし誤りがあれば、ご指摘いただけるとありがたいです。

今後は今回学んだ内容を基に、Hiltの学習により力を入れていきたいと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?