如何拆分大型复杂ViewModel或基于小型ViewModel构建主ViewModel?
拆分复杂ViewModel的实践方案
你的ViewModel目前承担了数据加载、过滤逻辑、状态管理等多个职责,随着功能迭代会越来越难维护。下面是几种拆分思路和重构示例:
一、按职责拆分独立子ViewModel
把不同业务模块的逻辑拆成单独的ViewModel,主ViewModel通过组合这些子ViewModel来协调工作,每个子ViewModel只负责单一职责:
- 库存数据子ViewModel:负责库存数据的加载、过滤逻辑
- 用户标签子ViewModel:负责用户标签的加载、选中状态管理
- 提交状态子ViewModel:负责提交按钮的可用性判断
二、重构后的代码示例
1. 库存数据子ViewModel
class InventoryViewModel : ViewModel() { private val tag = "MyApp${this.javaClass.simpleName}" private val allItems = mutableStateListOf<InventoryItemDto?>() private val _filterByName = MutableLiveData<String>("") val filterByName: LiveData<String> get() = _filterByName private val _filterByAssignedToMe = MutableLiveData<Boolean>(false) val filterByAssignedToMe: LiveData<Boolean> get() = _filterByAssignedToMe private val _displayedItems = MutableLiveData<List<InventoryItemDto?>>() val displayedItems: LiveData<List<InventoryItemDto?>> get() = _displayedItems private val _error = MutableLiveData<String>("") val error: LiveData<String> get() = _error init { loadInventoryItems() combineFilters() } fun setFilterByName(text: String) { _filterByName.value = text } fun setFilterByAssignedToMe(isChecked: Boolean) { _filterByAssignedToMe.value = isChecked } private fun combineFilters() { viewModelScope.launch { combine( filterByName.asFlow(), filterByAssignedToMe.asFlow() ) { nameFilter, assignedFilter -> applyFilters(nameFilter, assignedFilter) }.collect() } } private fun applyFilters(nameFilter: String, assignedFilter: Boolean) { Log.d(tag, "应用筛选:名称过滤=$nameFilter") val filtered = allItems.filter { item -> val matchesName = item?.name?.lowercase()?.contains(nameFilter.lowercase()) ?: false val matchesAssigned = if (assignedFilter) { // 补充"分配给我"的判断逻辑 true } else true matchesName && matchesAssigned } _displayedItems.value = filtered } private fun loadInventoryItems() { Log.d(tag, "加载库存数据...") viewModelScope.launch { try { val apiResponse = ApiResponse(ApiAdapter.apiClient.findAllInventoryItems()) if (apiResponse.code == 200 && apiResponse.body != null) { allItems.clear() allItems.addAll(apiResponse.body) applyFilters(_filterByName.value.orEmpty(), _filterByAssignedToMe.value ?: false) Log.d(tag, "库存数据加载完成") } else { _error.value = apiResponse.code.toString() Log.d(tag, "库存数据加载失败") } } catch (t: Throwable) { _error.value = t.message.toString() } } } }
2. 用户标签子ViewModel
class UserTagViewModel : ViewModel() { private val tag = "MyApp${this.javaClass.simpleName}" private val _userTags = MutableLiveData<List<UserTag>>() val userTags: LiveData<List<UserTag>> get() = _userTags private val _selectedUserTag = MutableLiveData<UserTag?>() val selectedUserTag: LiveData<UserTag?> get() = _selectedUserTag init { loadUserTags() } fun setSelectedUserTag(tag: UserTag?) { _selectedUserTag.value = tag } private fun loadUserTags() { // 替换为真实接口请求 val temp = mutableListOf<UserTag>() repeat(50) { val epc = getRandomHexString(24).uppercase() temp.add(UserTag(epc, 0, "0")) } viewModelScope.launch { delay(100) _userTags.value = temp Log.d(tag, "用户标签加载完成") } } private fun getRandomHexString(numchars: Int): String { val r = Random() val sb = StringBuffer() while (sb.length < numchars) { sb.append(Integer.toHexString(r.nextInt())) } return sb.toString().substring(0, numchars) } }
3. 提交状态子ViewModel
class SubmitStateViewModel( private val inventoryViewModel: InventoryViewModel, private val userTagViewModel: UserTagViewModel ) : ViewModel() { private val _canSubmit = MutableLiveData<Boolean>(false) val canSubmit: LiveData<Boolean> get() = _canSubmit init { observeSelectionChanges() } private fun observeSelectionChanges() { viewModelScope.launch { combine( inventoryViewModel.displayedItems.asFlow(), userTagViewModel.selectedUserTag.asFlow() ) { items, selectedTag -> // 判断是否可以提交:至少有一个选中的库存项和用户标签 val hasSelectedItem = items.any { /* 补充库存项选中判断逻辑 */ true } hasSelectedItem && selectedTag != null }.collect { canSubmit -> _canSubmit.value = canSubmit } } } }
4. 主ViewModel(组合子ViewModel)
class AssignUserTagToInventoryItemViewModel : ViewModel() { private val tag = "MyApp${this.javaClass.simpleName}" val inventoryViewModel = InventoryViewModel() val userTagViewModel = UserTagViewModel() val submitStateViewModel = SubmitStateViewModel(inventoryViewModel, userTagViewModel) init { Log.d(tag, "主ViewModel初始化完成") } }
三、额外优化建议
- 使用
mutableStateListOf替代ArrayList,配合Flow的combine操作符可以更优雅地处理多条件筛选,避免手动调用applyFilters - 每个子ViewModel只暴露必要的LiveData和方法,减少职责交叉
- 主ViewModel只负责协调子ViewModel,不处理具体业务逻辑
内容的提问来源于stack exchange,提问作者Kamil
相关产品推荐
相关产品推荐

