Что такое @Subcomponent в Dagger?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
@Subcomponent в Dagger: Иерархия зависимостей
@Subcomponent — это мощный инструмент Dagger для создания иерархии контейнеров зависимостей. Он позволяет создавать дочерние компоненты, которые имеют доступ к зависимостям родительского компонента.
Основное назначение
@Subcomponent — это Dagger компонент, который наследует все зависимости от родительского компонента и может добавлять свои собственные.
Когда использовать:
- Feature modules (каждый экран/фича = отдельный subcomponent)
- Разные scopes для разных частей приложения
- Временные графы зависимостей (например, для Activity/Fragment)
- Изоляция зависимостей feature'а
Сравнение @Component и @Subcomponent
// @Component — независимый граф зависимостей
@Component(modules = [AppModule::class])
interface AppComponent {
fun inject(app: MyApplication)
}
// @Subcomponent — дочерний граф (наследует от родителя)
@Subcomponent(modules = [ScreenModule::class])
interface ScreenComponent {
fun inject(screen: MyScreen)
}
Структура Subcomponent
Пример: App → Feature → Screen
// 1. Главный Component
@Component(modules = [AppModule::class])
interface AppComponent {
fun featureComponent(module: FeatureModule): FeatureComponent
}
// 2. Feature Subcomponent (наследует от App)
@Subcomponent(modules = [FeatureModule::class])
interface FeatureComponent {
fun screenComponent(module: ScreenModule): ScreenComponent
}
// 3. Screen Subcomponent (наследует от Feature)
@Subcomponent(modules = [ScreenModule::class])
interface ScreenComponent {
fun inject(screen: DetailScreen)
}
Объявление Subcomponent в родителе
Builder pattern (рекомендуется):
// Parent Component
@Component(modules = [AppModule::class])
interface AppComponent {
fun featureComponentBuilder(): FeatureComponent.Builder
}
// Subcomponent
@Subcomponent(modules = [FeatureModule::class])
interface FeatureComponent {
@Subcomponent.Builder
interface Builder {
fun module(module: FeatureModule): Builder
fun build(): FeatureComponent
}
fun inject(fragment: MyFragment)
}
// Использование
val appComponent = DaggerAppComponent.create()
val featureComponent = appComponent.featureComponentBuilder()
.module(FeatureModule())
.build()
featureComponent.inject(myFragment)
Доступ к зависимостям
Subcomponent может использовать:
- Все зависимости из App Component
- Свои зависимости из модулей
- Переопределить зависимости (override)
// App Module
@Module
class AppModule {
@Provides
@Singleton
fun provideDatabase(): Database = Database()
@Provides
@Singleton
fun provideUserRepository(db: Database): UserRepository {
return UserRepository(db)
}
}
// Feature Module — может использовать UserRepository из App
@Module
class FeatureModule {
@Provides
@FeatureScope
fun provideFeatureManager(repo: UserRepository): FeatureManager {
return FeatureManager(repo) // UserRepository из App!
}
}
// Feature Subcomponent
@FeatureScope
@Subcomponent(modules = [FeatureModule::class])
interface FeatureComponent {
fun inject(fragment: MyFragment)
}
Scopes в Subcomponent
Каждый subcomponent имеет свой scope:
// App Scope (Singleton для всего приложения)
@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class AppScope
// Feature Scope (для одного feature)
@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class FeatureScope
// Screen Scope (для одного экрана)
@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class ScreenScope
// Использование
@AppScope
@Component(modules = [AppModule::class])
interface AppComponent {
fun featureComponent(): FeatureComponent
}
@FeatureScope
@Subcomponent(modules = [FeatureModule::class])
interface FeatureComponent {
fun screenComponent(): ScreenComponent
}
@ScreenScope
@Subcomponent(modules = [ScreenModule::class])
interface ScreenComponent {
fun inject(activity: MyActivity)
}
Практический пример: Feature Module
// Модуль фичи (Feature)
@FeatureScope
@Subcomponent(modules = [UserFeatureModule::class])
interface UserFeatureComponent {
@Subcomponent.Factory
interface Factory {
fun create(module: UserFeatureModule): UserFeatureComponent
}
fun inject(fragment: UserListFragment)
fun inject(activity: UserDetailActivity)
}
// Модуль с провайдерами
@Module
class UserFeatureModule {
@Provides
@FeatureScope
fun provideUserListViewModel(repo: UserRepository): UserListViewModel {
return UserListViewModel(repo)
}
@Provides
@FeatureScope
fun provideUserDetailViewModel(repo: UserRepository): UserDetailViewModel {
return UserDetailViewModel(repo)
}
}
// Использование в приложении
class MyApplication : Application() {
val appComponent: AppComponent by lazy { DaggerAppComponent.create() }
fun getUserFeatureComponent(): UserFeatureComponent {
return appComponent.userFeatureComponentFactory()
.create(UserFeatureModule())
}
}
// В Fragment
class UserListFragment : Fragment() {
private lateinit var viewModel: UserListViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val app = requireActivity().application as MyApplication
val component = app.getUserFeatureComponent()
component.inject(this) // Inject зависимости
}
}
Преимущества Subcomponent
✅ Плюсы:
- Наследование зависимостей — не нужно передавать в каждый модуль
- Изоляция — feature имеет свой scope
- Type Safety — compile-time проверки
- Производительность — Dagger генерирует оптимальный код
- Иерархия — четкая структура приложения
❌ Минусы:
- Boilerplate — много кода для создания
- Coupling — subcomponent зависит от parent
- Сложность — может быть запутанным для новичков
Вывод
@Subcomponent — это идеальный инструмент для организации DI в модульных приложениях. Каждый feature/экран получает свой контейнер зависимостей, но может использовать зависимости родителя. Это обеспечивает чистую архитектуру и легкое тестирование.