【问题标题】:Kotlin: wrap ViewModel methods in objectsKotlin:将 ViewModel 方法包装在对象中
【发布时间】:2020-06-24 17:56:25
【问题描述】:

我遵循 MVVM 设计模式,并且我的一些视图模型最终具有相当多的方法来将实时数据公开给活动/片段。

我想知道我是否可以以某种方式“分组”这些 getter 方法,我想出了这个......

  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(...)
    }
}

你能告诉我这是否是包装方法的好方法吗?这是一个好/坏的做法吗?替代品?

谢谢

【问题讨论】:

  • 您能否澄清一下存储库和 UiFactory 函数的返回类型是什么?

标签: android kotlin mvvm viewmodel android-livedata


【解决方案1】:

我们有 MVI(或 MVVMI 在您的情况下)模式,可以帮助您对 VM 的公开数据进行分组。因此,您将拥有一个包含所有行值的 State 对象,而不是多个 LiveData。 Kotlin 的 Sealed Class 可以帮助您:

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 上的存储库中获取数据。

/** RAW DATA:直接从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(从您的代码中不清楚,但我假设您将数据存储在那里)。

您的方法可能是一个可行的解决方案,但一些平台的常见做法可以为您节省大量开发时间。此外,使用流行的设计方法可以简化团队中新成员的入职:)

【讨论】:

    猜你喜欢
    • 2012-07-31
    • 2012-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多