1. 问题现象:UseCase泛滥的架构困境
在Android Clean Architecture实践中,我见过最典型的反模式就是UseCase的滥用。很多团队机械地遵循"每个业务操作都要有独立UseCase"的原则,结果导致项目中冒出几百个UseCase类。最近接手的一个电商App里,仅购物车模块就有32个UseCase——AddToCartUseCase、UpdateCartItemUseCase、DeleteCartItemUseCase、GetCartCountUseCase...每个类里只有不到10行代码,却要维护完整的依赖注入链条。
这种情况下的代码库会出现几个明显症状:
- 类爆炸(Class Explosion):40%的代码是空壳UseCase
- 依赖地狱:每个UseCase都要注入Repository,导致Dagger/Hilt图变得异常复杂
- 逻辑碎片化:原本清晰的业务流被拆得七零八落
关键警示:当你的UseCase开始出现"GetXXX"、"UpdateXXX"这样的命名模式时,这已经是架构腐化的明显信号。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 问题根源:对Clean Architecture的误读
2.1 原始设计意图的偏离
Bob大叔提出Clean Architecture时,UseCase(Interactor)的定位是:
- 封装复杂业务规则
- 协调多个数据源的交互
- 处理线程调度等基础设施问题
而现实中的滥用往往源于两个误解:
- 把UseCase当作DTO传递工具
- 认为所有方法调用都需要UseCase包装
2.2 过度抽象的代价
以用户登录为例,对比健康结构和腐化结构:
健康实现:
kotlin复制class AuthUseCase @Inject constructor(
private val authRepo: AuthRepository,
private val userPrefs: UserPrefs
) {
suspend fun login(email: String, pwd: String): Result<User> {
// 验证输入格式
// 调用authRepo.login
// 处理token缓存
// 返回统一结果包装
}
}
腐化实现:
kotlin复制// 拆分成多个无意义的UseCase
class ValidateEmailUseCase { /* 只做邮箱格式检查 */ }
class LoginApiUseCase { /* 只调repo.login */ }
class SaveTokenUseCase { /* 只做存储 */ }
后者带来的问题:
- 业务逻辑被迫在ViewModel组合
- 无法有效处理错误和回滚
- 单元测试要mock多个UseCase
3. 重构方案:UseCase的合理边界
3.1 功能聚合原则
我的经验法则是:按业务场景而非CRUD操作划分UseCase。一个好的UseCase应该:
- 完成一个完整的业务目标(如"完成订单支付")
- 包含必要的校验和错误处理
- 管理自身范围内的数据一致性
3.2 代码组织建议
推荐按功能模块组织UseCase:
code复制features/
auth/
AuthUseCase.kt // 登录/注册/登出等聚合
checkout/
CheckoutUseCase.kt // 整合支付、优惠券、库存检查
3.3 性能优化技巧
对于高频调用的简单操作,可以适当突破架构约束:
kotlin复制// 在Repository接口直接暴露简单查询
interface UserRepository {
suspend fun getProfile(): UserProfile
// 允许绕过UseCase的简单getter
fun getNotificationEnabledFlow(): Flow<Boolean>
}
4. 架构演进:从僵化到灵活
4.1 分层架构的弹性实践
Clean Architecture的层级应该是指导而非约束。我现在的项目采用改良方案:
- 核心业务逻辑:严格UseCase隔离
- 简单数据流转:Repository直连ViewModel
- UI状态管理:部分逻辑下沉到Composable
4.2 测量架构健康度
引入两个量化指标:
- UseCase充实度 = 逻辑代码行数 / (类定义行数 + 注入代码行数)
- 低于1.5说明抽象过度
- 跨层调用率 = ViewModel直接调用Repository的次数 / 总调用次数
- 建议保持在10%-30%之间
5. 常见陷阱与规避策略
5.1 陷阱1:UseCase接口污染
反模式:
kotlin复制class OrderUseCase {
fun createOrder()
fun cancelOrder()
fun updateOrder()
fun listOrders()
//...20多个方法
}
解决方案:
- 按业务子域拆分(OrderCreationUseCase、OrderCancellationUseCase)
- 对于关联性强的操作使用密封类封装:
kotlin复制sealed class OrderCommand {
data class Create(val items: List<CartItem>) : OrderCommand()
data class Cancel(val orderId: String) : OrderCommand()
}
class OrderUseCase {
suspend fun execute(cmd: OrderCommand): Result<Order>
}
5.2 陷阱2:忽视线程上下文
很多团队忘记UseCase应该自己管理线程调度:
kotlin复制// 错误示范:把调度责任推给调用方
class ProductsUseCase(
private val repo: ProductRepository
) {
// 缺少调度器指定
suspend fun loadProducts(): List<Product> = repo.getProducts()
}
正确做法:
kotlin复制class ProductsUseCase @Inject constructor(
private val repo: ProductRepository,
@IoDispatcher private val dispatcher: CoroutineDispatcher
) {
suspend fun loadProducts(): List<Product> =
withContext(dispatcher) { repo.getProducts() }
}
6. 现代化架构的新思考
随着Kotlin Flow和Jetpack Compose的普及,UseCase的形态可以更加灵活:
6.1 响应式UseCase模式
kotlin复制class LocationUseCase(
private val locationClient: LocationClient
) {
val locations: Flow<Location> = locationClient.locations
.filter { it.accuracy < 50f }
.distinctUntilChanged()
.shareIn(scope, SharingStarted.WhileSubscribed(5000))
}
6.2 与Compose的集成
直接在UseCase中封装UI逻辑:
kotlin复制class FormValidationUseCase {
fun validateEmail(
email: String,
onError: (String) -> Unit
): Boolean {
if (!email.isValidEmail()) {
onError("Invalid email format")
return false
}
return true
}
}
在实践过程中,我发现架构模式的价值在于提供约束而非制造约束。当UseCase开始让代码变得更复杂而非更简单时,就是时候重新审视架构决策了。好的架构应该像水的容器——有形但无形,既能保持代码的整洁度,又能适应业务的快速变化。
