定義
定義は、Koinがどのように依存関係を作成し管理するかを宣言するものです。このガイドでは、DSLとアノテーションの両方を使用したすべての定義型について説明します。
定義の型
| 型 | DSL | アノテーション | ライフサイクル | ユースケース |
|---|---|---|---|---|
| シングルトン | single() | @Singleton | アプリの生存期間中、1つのインスタンス | サービス、リポジトリ、データベース |
| ファクトリ | factory() | @Factory | リクエストのたびに新しいインスタンス | プレゼンター、ユースケース、状態を持つオブジェクト |
| スコープ | scoped() | @Scoped | スコープごとに1つのインスタンス | Activityやセッションに紐づくオブジェクト |
| ViewModel | viewModel() | @KoinViewModel | Android ViewModelのライフサイクル | ViewModel |
定義の宣言
コンパイラプラグインDSL(推奨)
import org.koin.plugin.module.dsl.*
val appModule = module {
// シングルトン
single<Database>()
single<UserRepository>()
// ファクトリ - リクエストのたびに新しいインスタンスを作成
factory<UserPresenter>()
// ViewModel
viewModel<UserViewModel>()
}アノテーション
@Singleton // または @Single
class Database
@Singleton
class UserRepository(private val database: Database)
@Factory
class UserPresenter(private val repository: UserRepository)
@KoinViewModel
class UserViewModel(private val repository: UserRepository) : ViewModel()クラシックDSL
val appModule = module {
// コンストラクタ参照(自動ワイヤリング)を使用
singleOf(::Database)
singleOf(::UserRepository)
factoryOf(::UserPresenter)
viewModelOf(::UserViewModel)
// ラムダ(手動ワイヤリング)を使用
single { Database() }
single { UserRepository(get()) }
factory { UserPresenter(get()) }
viewModel { UserViewModel(get()) }
}定義の比較
| コンセプト | コンパイラプラグインDSL | クラシックDSL | アノテーション |
|---|---|---|---|
| シングルトン | single<MyClass>() | singleOf(::MyClass) | @Singleton / @Single |
| ファクトリ | factory<MyClass>() | factoryOf(::MyClass) | @Factory |
| スコープ | scoped<MyClass>() | scopedOf(::MyClass) | @Scoped |
| ViewModel | viewModel<MyVM>() | viewModelOf(::MyVM) | @KoinViewModel |
| Worker | worker<MyWorker>() | workerOf(::MyWorker) | @KoinWorker |
INFO
コンパイラプラグインはクラスや関数のパラメータを解析し、適切な get() 関数の呼び出しを自動生成します。そのため、手動で get() を記述する必要はありません。
Single(シングルトン)
アプリ全体で再利用される単一のインスタンスを作成します。
// DSL
single<DatabaseHelper>()
// アノテーション
@Singleton
class DatabaseHelperどちらも同じ結果、つまりすべてのコンシューマ(利用側)で共有される単一のインスタンスを作成します。
Factory(ファクトリ)
リクエストのたびに新しいインスタンスを作成します。
// DSL
factory<UserPresenter>()
// アノテーション
@Factory
class UserPresenter(private val repository: UserRepository)Scoped(スコープ)
スコープごとに1つのインスタンスを作成します。
// DSL
scope<MyActivity> {
scoped<ActivityPresenter>()
}
// アノテーション
@Scoped(MyActivityScope::class)
class ActivityPresenterViewModel
適切なライフサイクルを持つAndroid ViewModelです。
// DSL
viewModel<UserViewModel>()
// アノテーション
@KoinViewModel
class UserViewModel(private val repository: UserRepository) : ViewModel()インターフェースのバインド
コンパイラプラグインDSL
single<UserRepositoryImpl>() bind UserRepository::class
// 複数のバインド
single<MyServiceImpl>() binds arrayOf(ServiceA::class, ServiceB::class)クラシックDSL
singleOf(::UserRepositoryImpl) bind UserRepository::class
// またはラムダを使用
single<UserRepository> { UserRepositoryImpl(get()) }アノテーション
クラスがインターフェースを実装している場合、インターフェースのバインドは自動的に行われます。
@Singleton
class UserRepositoryImpl(
private val database: Database
) : UserRepository // 自動的に UserRepository にバインドされる明示的にバインドする場合:
@Singleton
@Binds(UserRepository::class)
class UserRepositoryImpl : UserRepositoryクオリファイア(名前の指定された定義)
同じ型の定義が複数ある場合に使用します。取得方法については、クオリファイアを使用した注入も参照してください。
コンパイラプラグインDSL
コンパイラプラグインDSLで文字列のクオリファイアを使用する場合(以前 named() を使用していたときのように)、@Named でアノテーションを付ける必要があります。
@Named("local")
class LocalDatabase : Database
@Named("remote")
class RemoteDatabase : Database
class UserRepository(
@Named("local") private val localDb: Database,
@Named("remote") private val remoteDb: Database
)
single<LocalDatabase>()
single<RemoteDatabase>()
single<UserRepository>()
// 使用方法
val localDb: Database = get(named("local"))クラシックDSL
single<Database>(named("local")) { LocalDatabase() }
single<Database>(named("remote")) { RemoteDatabase() }
// 使用方法
val localDb: Database = get(named("local"))アノテーション
@Singleton
@Named("local")
class LocalDatabase : Database
@Singleton
@Named("remote")
class RemoteDatabase : Database
// コンシューマ(利用側)での例
@Singleton
class UserRepository(
@Named("local") private val localDb: Database,
@Named("remote") private val remoteDb: Database
)注入パラメータ
注入時にパラメータを渡します。
コンパイラプラグインDSL
@InjectedParam を使用して、そのパラメータが注入パラメータ(injected parameters)によって提供されることを示します。
class UserPresenter(
@InjectedParam userId : String,
repository : UserRepository
)
factory<UserPresenter>()クラシックDSL
class UserPresenter(
userId : String,
repository : UserRepository
)
factory { params ->
UserPresenter(
userId = params.get(),
repository = get()
)
}アノテーション
@Factory
class UserPresenter(
@InjectedParam val userId: String,
val repository: UserRepository // 自動注入される
)
// 使用方法
val presenter: UserPresenter = get { parametersOf("user123") }オプショナルな依存関係
コンパイラプラグインDSL
class MyService(
val required: RequiredDep,
val optional: OptionalDep? // getOrNull() で解決される
)
single<MyService>()クラシックDSL
single {
MyService(
required = get(),
optional = getOrNull()
)
}アノテーション
Null許容(Nullable)なパラメータは自動的に処理されます。
@Singleton
class MyService(
val required: RequiredDep,
val optional: OptionalDep? // getOrNull() で解決される
)遅延注入
インスタンスの作成を遅延させます。
コンパイラプラグインDSL
class MyService(
val lazyDep: Lazy<HeavyDependency> // 遅延作成
)
single<MyService>()クラシックDSL
single {
MyService(
lazyDep = inject() // Lazy<Dependency>
)
}アノテーション
@Singleton
class MyService(
val lazyDep: Lazy<HeavyDependency> // 遅延作成
)プロパティ
設定値を注入します。
コンパイラプラグインDSL
class ApiClient(
@Property("api_url") val url: String,
@Property("api_key") val key: String
)
single<ApiClient>()クラシックDSL
single {
ApiClient(
url = getProperty("api_url"),
key = getProperty("api_key", "default")
)
}アノテーション
@Singleton
class ApiClient(
@Property("api_url") val url: String,
@Property("api_key") val key: String
)コールバック
onClose コールバック
インスタンスが解放されるときにコードを実行します。
single {
Database()
} onClose {
it?.close() // Koinの停止時、またはスコープの終了時に呼び出される
}createdAtStart
起動時にインスタンスを先行作成(eager creation)します。
// コンパイラプラグインDSL
single<ConfigManager>() withOptions {
createdAtStart()
}
// クラシックDSL
single(createdAtStart = true) {
ConfigManager()
}定義のオーバーライド
デフォルト:後勝ち(Last Wins)
val prodModule = module {
single<ApiService> { ProductionApi() }
}
val testModule = module {
single<ApiService> { MockApi() } // 本番用をオーバーライドする
}
startKoin {
modules(prodModule, testModule)
}明示的なオーバーライド
厳密モード(strict mode)では、オーバーライドを明示的にマークします。
val testModule = module {
single<ApiService> { MockApi() }.override()
}
startKoin {
allowOverride(false)
modules(prodModule, testModule)
}ベストプラクティス
- コンストラクタ注入を優先する - Koinなしでコードをテスト可能にします。
- 状態を持たないサービスには
singleを使用する - リポジトリ、クライアント、ヘルパーなど。 - 状態を持つオブジェクトには
factoryを使用する - 状態を持つプレゼンター、ユースケースなど。 - ライフサイクルに紐づくオブジェクトには
scopedを使用する - Activity、Fragment、セッションなど。 - クオリファイアの使用を最小限に抑える - 可能な場合は代わりに異なるインターフェースを使用します。
- インターフェースにバインドする - 実装ではなく抽象に依存させます。
- 外部ライブラリには
create(::builder)を使用する - より安全な依存関係の解決が可能です。
次のステップ
- 注入 - 依存関係の取得
- クオリファイア - 名前付きおよび型付きのクオリファイア
- アドバンスドパターン - コレクション、デコレータ、外部ライブラリ
- スコープ - ライフサイクルの管理
