依存性注入(DI)とは?KotlinとHilt・Koinで実践【Kotlin入門⑯】

依存性注入(DI)とは?

DIの概要

依存性注入(Dependency Injection, DI)とは、オブジェクトの依存関係を外部から注入する設計パターンです。DIを活用することで、以下のメリットが得られます。

  • コードの再利用性向上:異なるコンポーネント間での依存関係を分離できる。
  • テスト容易性の向上:モックやスタブを簡単に差し替えられる。
  • 保守性の向上:依存関係を明示的に管理しやすくなる。

DIの基本概念

DIには主に以下の3つの方法があります。

  1. コンストラクタインジェクション(推奨)
  2. フィールドインジェクション
  3. メソッドインジェクション

Kotlinでは、主に コンストラクタインジェクション が推奨されます。

class Engine
class Car(private val engine: Engine) {
    fun start() {
        println("Car is starting with engine: $engine")
    }
}

この例では、Car クラスのコンストラクタで Engine インスタンスを受け取り、依存性を注入しています。

Hiltによる依存性注入

Hiltとは?

Hiltは、Googleが提供する Dagger ベースの依存性注入フレームワークで、AndroidアプリのDIを簡単に実装できます。

Hiltのメリット

  • 公式サポート:GoogleがAndroid向けに最適化。
  • コードの簡潔化:Daggerよりも設定がシンプル。
  • ライフサイクルに統合:ActivityやFragmentに簡単に適用可能。

Hiltのセットアップ

build.gradle.kts に以下を追加します。

dependencies {
    implementation("com.google.dagger:hilt-android:2.40.5")
    kapt("com.google.dagger:hilt-compiler:2.40.5")
}

アプリの Application クラスに @HiltAndroidApp を追加します。

@HiltAndroidApp
class MyApplication : Application()

Hiltを使った依存性注入

モジュールの定義

Hiltでは、@Module@InstallIn アノテーションを使って依存関係を提供します。

@Module
@InstallIn(SingletonComponent::class)
object AppModule {
    @Provides
    fun provideEngine(): Engine {
        return Engine()
    }
}

コンストラクタインジェクション

@Inject アノテーションを使用して依存性を注入できます。

class Car @Inject constructor(private val engine: Engine) {
    fun start() {
        println("Car is starting with engine: $engine")
    }
}

Activityへの注入

ActivityやFragmentで @AndroidEntryPoint を追加し、依存性を注入できます。

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject lateinit var car: Car
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        car.start()
    }
}

Koinによる依存性注入

Koinとは?

Koinは、シンプルなDIフレームワークで、コードのボイラープレートを最小限に抑えられるのが特徴です。

Koinのメリット

  • シンプルなAPI:アノテーション不要。
  • 設定が簡単:Hiltよりも導入が容易。
  • 軽量:Kotlinネイティブにも対応。

Koinのセットアップ

build.gradle.kts に以下を追加します。

dependencies {
    implementation("io.insert-koin:koin-android:3.1.4")
}

アプリの Application クラスで Koin を初期化します。

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

Koinの使用方法

モジュールの定義

Koinでは module を使用して依存関係を提供します。

val appModule = module {
    single { Engine() }
    factory { Car(get()) }
}

Activityへの依存性注入

by inject() を使用すると、簡単に依存関係を取得できます。

class MainActivity : AppCompatActivity() {
    private val car: Car by inject()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        car.start()
    }
}

HiltとKoinの比較

比較項目HiltKoin
設定の簡単さやや複雑シンプル
パフォーマンス高速(Daggerベース)やや低速(リフレクション使用)
Kotlinネイティブ対応なしあり
ボイラープレートの量多め少なめ

HiltはGoogle公式でサポートされており、大規模プロジェクトに向いています。一方、Koinはシンプルな設定が可能で、より軽量なプロジェクトに適しています。

まとめ

本記事では、Kotlinにおける依存性注入(DI)の基本と、Hilt・Koinの導入方法について解説しました。

  • DIを使うことで、依存関係を明確化し、コードの保守性を向上させる
  • HiltはDaggerベースで公式サポートがあり、Androidアプリに最適
  • Koinはシンプルで設定が容易、Kotlinネイティブにも対応

プロジェクトの規模や要件に応じて、適切なDIフレームワークを選択しましょう!