← Назад к вопросам

Что такое @Subcomponent в Dagger?

2.0 Middle🔥 141 комментариев
#Dependency Injection#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

@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/экран получает свой контейнер зависимостей, но может использовать зависимости родителя. Это обеспечивает чистую архитектуру и легкое тестирование.