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

Как доставляешь репозиторий в Viewmodel

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

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

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

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

Подход к доставке репозитория во ViewModel в Android приложении

При разработке приложений с использованием Clean Architecture или MVVM, доставка репозитория во ViewModel — критически важный процесс для обеспечения тестируемости, гибкости и соблюдения принципа инверсии зависимостей.

Основные методы внедрения зависимостей

1. Через конструктор ViewModel (наиболее предпочтительный)

Наиболее чистый и тестируемый способ — передача зависимости через конструктор ViewModel с использованием Dependency Injection (DI) фреймворков.

class MyViewModel(
    private val myRepository: MyRepository
) : ViewModel() {
    
    fun loadData() {
        viewModelScope.launch {
            val data = myRepository.getData()
            // Обработка данных
        }
    }
}

Для внедрения через Dagger/Hilt:

@HiltViewModel
class MyViewModel @Inject constructor(
    private val myRepository: MyRepository
) : ViewModel() {
    // ViewModel логика
}

2. Использование фабрики ViewModelProvider.Factory

При необходимости передавать параметры или использовать внедрение зависимостей без аннотаций:

class MyViewModelFactory(
    private val myRepository: MyRepository
) : ViewModelProvider.Factory {
    
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return MyViewModel(myRepository) as T
    }
}

Использование в Activity/Fragment:

val factory = MyViewModelFactory(repository)
val viewModel = ViewModelProvider(this, factory).get(MyViewModel::class.java)

Архитектурные подходы и лучшие практики

Принципы, которых я придерживаюсь:

  1. Инверсия управления — ViewModel зависит от абстракции (интерфейса репозитория), а не от конкретной реализации:
interface UserRepository {
    suspend fun getUser(id: String): User
}

class UserViewModel @Inject constructor(
    private val repository: UserRepository
) : ViewModel()
  1. Ленивая инициализация при необходимости:
class MyViewModel @Inject constructor(
    private val repositoryProvider: Provider<MyRepository>
) : ViewModel() {
    
    private val repository by lazy { repositoryProvider.get() }
}
  1. Scoped зависимости с использованием Dagger/Hilt для правильного жизненного цикла:
@InstallIn(ViewModelComponent::class)
@Module
object ViewModelModule {
    
    @Provides
    fun provideRepository(
        apiService: ApiService,
        localDataSource: LocalDataSource
    ): MyRepository {
        return MyRepositoryImpl(apiService, localDataSource)
    }
}

Пример полной реализации с Dagger/Hilt:

// 1. Интерфейс репозитория
interface DataRepository {
    suspend fun fetchData(): List<Data>
}

// 2. Реализация репозитория
class DataRepositoryImpl @Inject constructor(
    private val apiService: ApiService,
    private val cache: DataCache
) : DataRepository {
    override suspend fun fetchData() = apiService.getData()
}

// 3. ViewModel с внедренной зависимостью
@HiltViewModel
class DataViewModel @Inject constructor(
    private val repository: DataRepository
) : ViewModel() {
    
    private val _data = MutableStateFlow<List<Data>>(emptyList())
    val data: StateFlow<List<Data>> = _data.asStateFlow()
    
    fun loadData() {
        viewModelScope.launch {
            _data.value = repository.fetchData()
        }
    }
}

Почему именно такой подход?

  1. Тестируемость — можно легко подменять реальный репозиторий моком в unit-тестах
  2. Гибкость — изменение реализации репозитория не затрагивает ViewModel
  3. Соблюдение SOLID — принцип инверсии зависимостей и открытости/закрытости
  4. Управление жизненным циклом — зависимости живут столько же, сколько и ViewModel

Альтернативные подходы (менее предпочтительные)

  • Service Locator через Application класс — нарушает инверсию зависимостей
  • Создание репозитория напрямую в ViewModel — усложняет тестирование и нарушает DI
  • Singleton-репозитории — могут вызывать проблемы с памятью и жизненным циклом

Ключевой вывод: Использование dependency injection через конструктор ViewModel с поддержкой DI фреймворка — наиболее профессиональный и поддерживаемый подход в современной Android разработке. Это обеспечивает чистоту архитектуры, простоту тестирования и масштабируемость приложения.

Как доставляешь репозиторий в Viewmodel | PrepBro