我遵循的是MVVM设计模式,而我的某些视图模型最终采用了很多方法将活动数据暴露给活动/片段。
我想知道是否可以将这些吸气剂方法“分组”,然后我想到了这个……
class MyViewModel(private val repository: MyRepository) : ViewModel() {
/** RAW DATA: data received directly from the api **/
private val apiData1 = repository.getApiData1()
private val apiData2 = repository.getApiData2()
inner class ApiData {
// Expose only api data 1
fun getApiData1() = apiData1
}
/** UI DATA: data conveniently formatted for the UI **/
private var uiDataA = UiFactory.buildUiDataA(apiData1)
private var uiDataB = UiFactory.buildUiDataB(apiData2)
private var uiDataC = UiFactory.buildUiDataC(apiData1, apiData2)
inner class UiData {
// Expose all ui data
fun getUiDataA() = uiDataA
fun getUiDataB() = uiDataB
fun getUiDataC() = uiDataC
}
}
那样,如果我想访问一个方法,就需要实例化其特定的包装器。
对于原始数据:viewModel.RawData().getApiData1()
对于ui数据:viewModel.UiData().getUiDataA()
class MyActivity {
val vm: MyViewModel = ... // Load view model:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
vm.UiData().getUiDataA().observe(...)
}
}
您能告诉我这是否是包装方法的好方法吗?这是好/坏做法吗?替代品?
谢谢
答案 0 :(得分:0)
有我们的MVI(或您的情况下的MVVMI)模式可以帮助您对VM的公开数据进行分组。因此,您将拥有一个包含所有行值的State对象,而不是使用多个LiveData
。 Kotlin的密封班可以为您提供帮助:
sealed class UiState {
data class Data(
val uiDataA: Any,
val uiDataB: Any,
val uiDataC: Any
) : UiState()
object Loading : UiState()
data class Error(val cause: Throwable) : UiState()
}
在您的代码中,您将从VM init上的存储库中获取数据。
/ **原始数据:直接从api接收的数据** /
私有val apiData1 = repository.getApiData1()
我建议您避免这种情况,并且仅根据视图需求或在后台(例如,使用协程)仅延迟获取所有数据
class MyViewModel(
private val uiService: UiFactory,
private val getRawDataUseCase: GetRawApiDataUseCase
) : ViewModel() {
/** RAW DATA: data received directly from the api **/
fun fetchApiData() = getRawDataUseCase() // TODO: make it async
/** UI DATA: data conveniently formatted for the UI **/
fun fetchUiData() = liveData<UiState> {
emit(UiState.Loading)
try {
emit(UiState.Data(
uiDataA = uiService.buildUiDataA(),
uiDataB = uiService.buildUiDataB(),
uiDataC = uiService.buildUiDataC()
))
} catch (t:Throwable) {
emit(UiState.Error(t))
}
}
}
在UiFactory
的干净架构中,功能基本上是用例。您可以将类拆分为一堆用例类,也可以保持不变。在干净的体系结构中,您的UiFactory
将是Service。
最好不要从VM(表示层)访问存储库(数据层)。因此您的域层将连接它们,并将包含2个类:
class UiFactory(private val repository: MyRepository) { //better name it UiService
fun buildUiDataA(): Any {
val apiData1 = repository.getApiData1()
// conveniently format your data apiData1
}
fun buildUiDataB(): Any {
val apiData2 = repository.getApiData1()
// conveniently format your data apiData1
}
fun buildUiDataC(): Any {
// you can store your apiData1 and apiData2 in this class to not duplicate
// repository calls
}
}
class GetRawApiDataUseCase(private val repository: MyRepository) {
operator fun invoke() = repository.getApiData1()
}
然后您的活动将处理以下状态:
class MyActivity {
val vm: MyViewModel = ... // Load view model:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
vm.fetchUiData().observe {
when(it) {
is UiState.Data -> {
display(it.uiDataA)
display(it.uiDataB)
display(it.uiDataC)
//display data
}
is UiState.Error -> displayError(it.cause)
UiState.Loading -> showLoading()
}
}
}
}
顺便说一句,it's not the best practice to store LiveData in your repository(您的代码尚不清楚,但我假设您将数据存储在此处)。
您的方法可能是可行的解决方案,但是某些平台的常规做法可以为您节省大量开发时间。而且,使用流行的设计方法可以简化团队中新成员的加入:)