You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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,ViewModel接收这个Result后再转换成UI状态。原因很简单:

  • 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

火山引擎 最新活动