UGA Boxxx

つぶやきの延長のつもりで、知ったこと思ったこと書いてます

【Kotlin】Koinのby inject()とコンストラクタ注入の違い

Koinにはコンストラクタ注入もあることを知った

コンストラクタ注入とは、コンストラクタを通じて依存関係を直接渡す形で注入すること

コンストラクタ注入の例

1. 依存関係となるクラス

まず、MyRepositoryMyServiceという2つのクラスを定義します。MyServiceMyRepositoryに依存している

class MyRepository {
    fun getData(): String = "Hello from MyRepository"
}

class MyService(private val repository: MyRepository) {
    fun sayHello(): String {
        return repository.getData()
    }
}

ここで、MyServiceはコンストラクタを通じてMyRepositoryを受け取っている

このようにコンストラクタで依存関係を指定することで、依存関係の注入を自然な形で表現できる

2. Koinモジュールで依存関係を定義

次に、Koinのmoduleを使って依存関係を定義する

singleを使ってシングルトンとしてインスタンスを提供する

val appModule = module {
    single { MyRepository() }
    single { MyService(get()) }  // コンストラクタに依存関係を注入
}

ここでMyService(get())get()は、KoinがMyRepositoryインスタンスを提供してくれることを意味する

3. Koinの開始

アプリケーションのエントリーポイントでKoinを開始する

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin {
            androidContext(this@MyApplication)
            modules(appModule)
        }
    }
}

4. ViewModelや他のクラスでの利用

次に、Koinを使ってMyServiceを注入

コンストラクタでの注入を利用しているので、Koinは自動的に依存関係を解決する

class MyViewModel(private val myService: MyService) : ViewModel() {
    fun displayMessage(): String {
        return myService.sayHello()
    }
}

🎯 by inject() とコンストラクタ注入の違い

by inject()

by inject()はKoinが提供するプロパティデリゲートを利用したフィールドインジェクション

これは、クラスの中で後から依存関係を注入したいときに使う

class MyViewModel : ViewModel() {
    private val myService: MyService by inject()  // by inject()で依存関係を遅延注入
}
  • 特徴:

    • 遅延初期化: by inject()インスタンスが初めて使われるタイミングでKoinが依存関係を解決する。つまり、インジェクションのタイミングをコントロールできる
    • 依存関係が多いクラスに便利: コンストラクタが大きくならないため、依存関係が多い場合には使いやすい
    • プロパティとして保持: インジェクションされたオブジェクトをプロパティとして扱いたい場合に便利
  • 使い所:

    • 少数の依存関係がある場合や、テストやモックを使わないクラスで使用することが多い
    • ViewModelやアクティビティなどAndroidライフサイクルに依存したクラスで、コンストラクタに依存関係を渡すのが難しい場合に便利

コンストラクタ注入

依存するオブジェクトを明示的にコンストラクタで受け取るため、クラスの設計がクリーンになる

class MyViewModel(private val myService: MyService) : ViewModel() {
    fun displayMessage(): String {
        return myService.sayHello()
    }
}
  • 特徴:

    • 明示的な依存関係の宣言: 依存関係がどのクラスに必要かが一目でわかり、クラスの責務が明確になる
    • テストしやすい: コンストラクタ注入はテストの際に依存関係を容易にモックやスタブに置き換えることができる
    • 即時注入: インスタンスが生成されるときに依存関係がすぐに提供される。遅延初期化ではないため、依存関係が確実に必要な場合に使われる
  • 使い所:

    • テストやモックが必要なクラスや、依存関係が多いクラスに適している
    • 依存関係の注入をクラスの設計に統合したい場合、コンストラクタで注入することでクラスの責務をより明確にできる

使い分け

by inject()を使うべき場合

  • 遅延初期化が必要な場合、またはインスタンスが使われない限りオブジェクトを生成したくない場合

コンストラクタ注入を使うべき場合

  • テスト可能なコードを書く場合。依存関係が明示されているため、テスト時にモックやスタブを渡すのが容易
  • 依存関係が多い場合。依存オブジェクトのライフサイクルや生成順序を制御しやすく、クラスの設計もシンプルになる
  • ビジネスロジッククラスやサービスクラス。依存関係を明示的に宣言し、クラスをよりテストしやすく保つためにコンストラクタ注入が推奨される