Android Clean架构中UseCase的作用及Result<T>返回层级的技术咨询
Android Clean架构中UseCase的作用及Result返回层级的技术咨询
嘿,我完全懂你现在的困惑——刚接触Clean架构时,很多人都会疑惑UseCase层到底存在的意义是什么,毕竟看起来ViewModel直接调用仓库也能完成功能。咱们好好唠唠这个问题,再说说Result
首先,UseCase到底有啥用?
你提到的ViewModel直接调用仓库确实能跑,但从长期维护、代码复用和架构合理性来看,UseCase层绝对不是多余的,它的核心价值体现在这几点:
- 单一职责与业务逻辑隔离:仓库的职责是数据层的操作(比如从数据库/网络拿数据、存数据),而UseCase是特定业务场景的逻辑封装。比如你需要同时获取用户信息和他的玩具列表,这个“关联查询并组装数据”的业务逻辑就该放在UseCase里,而不是塞进ViewModel。ViewModel的职责是处理UI状态、响应用户交互,要是把业务逻辑都堆进去,很快ViewModel就会变成“大杂烩”,难以维护和修改。
- 代码复用:很多业务逻辑会被多个ViewModel用到,比如“获取当前登录用户信息”这个逻辑,可能在个人中心、设置页、订单页的ViewModel里都需要。把它封装成UseCase,各个ViewModel直接调用就行,不用重复写相同的代码。
- 测试更简单:UseCase是纯业务逻辑,依赖的仓库可以用Mock对象替代,你可以单独测试业务逻辑的正确性,不用考虑ViewModel的生命周期、UI状态这些额外因素。而如果把业务逻辑放在ViewModel里,测试起来会复杂很多。
- 统一错误处理:你说的错误捕获,ViewModel确实能做,但把错误处理统一放在UseCase里,能让ViewModel更专注于UI状态的分发。比如所有UseCase都统一返回Result
,ViewModel只需要根据Result的状态来更新UI(成功显示数据,失败显示错误提示),不用每个ViewModel都写重复的try-catch逻辑。
然后说说Result该在哪返回?
最佳实践是让UseCase返回Result
- UseCase是业务逻辑的执行者,它最清楚业务执行过程中可能出现的异常(比如网络请求失败、数据不存在、权限不足),由它来封装Result
是最合理的。 - ViewModel的职责是把业务结果转换成UI能识别的状态,比如把Result.Success转换成带有数据的UiState.Success,把Result.Failure转换成带有错误信息的UiState.Error,然后分发给UI层。
看你给出的代码示例,GetUserByIdUseCase返回Result<User>的做法完全正确,咱们可以再补全ViewModel的逻辑,让整个流程更清晰:
// 先定义一个UI状态密封类 sealed class UiState<out T> { object Loading : UiState<Nothing>() data class Success<out T>(val data: T) : UiState<T>() data class Error(val message: String) : UiState<Nothing>() } class SomeViewModel @Inject constructor( private val savedStateHandle: SavedStateHandle, private val getUserByIdUseCase: GetUserByIdUseCase ) : ViewModel() { // 用StateFlow暴露UI状态给UI层 private val _userState = MutableStateFlow<UiState<User>>(UiState.Loading) val userState = _userState.asStateFlow() fun fetchUser(userId: Int) { viewModelScope.launch { val result = getUserByIdUseCase(userId) // 把Result转换成UI状态 _userState.value = when (result) { is Result.Success -> UiState.Success(result.data) is Result.Failure -> UiState.Error(result.exception.message ?: "获取用户信息失败") } } } } class GetUserByIdUseCase @Inject constructor( private val userRepository: UserRepository ) { suspend operator fun invoke(id: Int): Result<User> { return try { val user = userRepository.getUserById(id) Result.success(user) } catch (e: Exception) { Result.failure(e) } } }
最后总结一下
- UseCase层是Clean架构中业务逻辑的封装容器,它让代码职责更清晰、更易复用和测试,绝对不是多余的。
- Result
应该由UseCase返回,ViewModel负责将其转换成UI状态,这样每个层级的职责划分明确,完全符合Clean架构“关注点分离”的核心思想。
内容来源于stack exchange




