Контекст
Итак, я работал с архитектурой MVVM всего в паре проектов. Я все еще пытаюсь понять и улучшить, как работает архитектура. Я всегда работал с архитектурой MVP, используя обычный набор инструментов, Dagger для DI, обычно многомодульные проекты, уровень Presenter вводился с кучей Interactors / UseCases, и каждый Interactor вводился с разными репозиториями для выполнения вызовов backend API .
Теперь, когда я перешел в MVVM, я изменил уровень Presenter с помощью ViewModel, связь от ViewModel со слоем пользовательского интерфейса осуществляется через LiveData вместо использования интерфейса обратного вызова View и так далее.
Выглядит так:
class ProductDetailViewModel @inject constructor(
private val getProductsUseCase: GetProductsUseCase,
private val getUserInfoUseCase: GetUserInfoUseCase,
) : ViewModel(), GetProductsUseCase.Callback, GetUserInfoUseCase.Callback {
// Sealed class used to represent the state of the ViewModel
sealed class ProductDetailViewState {
data class UserInfoFetched(
val userInfo: UserInfo
) : ProductDetailViewState(),
data class ProductListFetched(
val products: List<Product>
) : ProductDetailViewState(),
object ErrorFetchingInfo : ProductDetailViewState()
object LoadingInfo : ProductDetailViewState()
}
...
// Live data to communicate back with the UI layer
val state = MutableLiveData<ProductDetailViewState>()
...
// region Implementation of the UseCases callbacks
override fun onSuccessfullyFetchedProducts(products: List<Product>) {
state.value = ProductDetailViewState.ProductListFetched(products)
}
override fun onErrorFetchingProducts(e: Exception) {
state.value = ProductDetailViewState.ErrorFetchingInfo
}
override fun onSuccessfullyFetchedUserInfo(userInfo: UserInfo) {
state.value = ProductDetailViewState.UserInfoFetched(userInfo)
}
override fun onErrorFetchingUserInfo(e: Exception) {
state.value = ProductDetailViewState.ErrorFetchingInfo
}
// Functions to call the UseCases from the UI layer
fun fetchUserProductInfo() {
state.value = ProductDetailViewState.LoadingInfo
getProductsUseCase.execute(this)
getUserInfoUseCase.execute(this)
}
}
Здесь нет ракетостроения, иногда я меняю реализацию, чтобы использовать более одного свойства LiveData для отслеживания изменений. Кстати, это всего лишь пример, который я написал на лету, поэтому не ждите, что он скомпилируется. Но просто ViewModel вводится с кучей UseCases, он реализует интерфейсы обратного вызова UseCases, и когда я получаю результаты от UseCases, я передаю их на уровень пользовательского интерфейса через LiveData.
Мои варианты использования обычно выглядят так:
// UseCase interface
interface GetProductsUseCase {
interface Callback {
fun onSuccessfullyFetchedProducts(products: List<Product>)
fun onErrorFetchingProducts(e: Exception)
}
fun execute(callback: Callback)
}
// Actual implementation
class GetProductsUseCaseImpl(
private val productRepository: ApiProductRepostory
) : GetProductsUseCase {
override fun execute(callback: Callback) {
productRepository.fetchProducts() // Fetches the products from the backend through Retrofit
.subscribe(
{
// onNext()
callback.onSuccessfullyFetchedProducts(it)
},
{
// onError()
callback.onErrorFetchingProducts(it)
}
)
}
}
Классы My Repository обычно являются оболочками для экземпляра Retrofit, и они заботятся о настройке правильного планировщика, чтобы все работало в правильном потоке, и отображении ответов серверной части в классы модели. Под ответами серверной части я подразумеваю классы, сопоставленные с Gson (например, список ApiProductResponse), и они сопоставляются с классами модели (например, Список продуктов, который я использую в приложении)
Вопрос
Мой вопрос здесь в том, что с тех пор, как я начал работать с архитектурой MVVM, все статьи и все примеры, люди либо вводят репозитории прямо в ViewModel (дублируя код для обработки ошибок и сопоставления ответов), либо используют единый источник истины. шаблон (получение информации из комнаты с помощью Flowables комнаты). Но я не видел, чтобы кто-нибудь использовал варианты использования со слоем ViewModel. Я имею в виду, что это довольно удобно, я могу держать вещи разделенными, я занимаюсь отображением ответов серверной части в рамках UseCases, я обрабатываю любые ошибки там. Но все же есть вероятность, что я не вижу, чтобы кто-то этим занимался, есть ли способ улучшить UseCases, чтобы сделать их более удобными для ViewModels с точки зрения API? Осуществлять связь между вариантами использования и моделями просмотра с помощью чего-то еще, кроме интерфейса обратного вызова?
Пожалуйста, дайте мне знать, если вам понадобится дополнительная информация об этом. Извините за примеры, я знаю, что они не самые лучшие, я просто придумал что-то простое, чтобы лучше это объяснить.
Спасибо,
Изменить №1
Вот как выглядят мои классы репозитория:
// ApiProductRepository interface
interface ApiProductRepository {
fun fetchProducts(): Single<NetworkResponse<List<ApiProductResponse>>>
}
// Actual implementation
class ApiProductRepositoryImpl(
private val retrofitApi: ApiProducts, // This is a Retrofit API interface
private val uiScheduler: Scheduler, // AndroidSchedulers.mainThread()
private val backgroundScheduler: Scheduler, // Schedulers.io()
) : GetProductsUseCase {
override fun fetchProducts(): Single<NetworkResponse<List<ApiProductResponse>>> {
return retrofitApi.fetchProducts() // Does the API call using the Retrofit interface. I've the RxAdapter set.
.wrapOnNetworkResponse() // Extended function that converts the Retrofit's Response object into a NetworkResponse class
.observeOn(uiScheduler)
.subscribeOn(backgroundScheduler)
}
}
// The network response class is a class that just carries the Retrofit's Response class status code
fun fetchOrders(): Observable<List<Order>> {...
Затем в ViewModel я выполняю:fetchOrdersInteractor.fetchOrders().subscribe {....}
Что-то вроде этого. - person 4gus71n   schedule 07.10.2019