You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用ViewModel与Clean Code时,UI展示用的数据格式化逻辑应放在何处?

方案选择结论

优先选择方案1(格式化逻辑放在View层实现,保持ViewModel无Context依赖),无论从架构合理性还是测试效率角度都比方案2更优,具体分析如下:

方案2的核心问题

  • 违背ViewModel的设计原则:ViewModel的核心设计目标是与Android Framework层解耦,即便注入不会内存泄漏的Application Context,也会让ViewModel依赖Android平台组件,无法运行纯JVM单元测试。插桩测试的执行速度比单元测试慢10倍以上,长期会大幅降低测试迭代效率。
  • 不符合关注点分离:格式化访问drawable、字符串等操作属于UI渲染范畴的逻辑,本身就不该由不感知UI细节的ViewModel负责。

方案1的优化实现(避免View层代码臃肿)

不需要把所有格式化逻辑都堆在Activity/Fragment中,可以通过分层拆分保持代码整洁:

  • 抽离独立的UI格式化工具类,统一存放依赖Context的资源转换逻辑,View层只需要传入ViewModel提供的原始数据即可拿到格式化结果,不会产生冗余代码。
  • 对于需要跨页面复用的格式化逻辑,可以封装为公共UI层的扩展方法,所有View层组件都可以直接调用。
  • 可引入TextResource中间层做折中:如果需要ViewModel明确指定使用的资源ID,可参考以下封装实现,让ViewModel不需要依赖Context就能传递资源信息:
sealed class TextResource {
    // 直接返回原始字符串
    data class RawText(val content: String) : TextResource()
    // 返回字符串资源ID + 占位参数
    data class ResText(@StringRes val resId: Int, val args: List<Any> = emptyList()) : TextResource()
}

View层只需要实现一个简单的转换方法,就能把TextResource转为实际渲染的字符串:

fun TextResource.asString(context: Context): String {
    return when(this) {
        is RawText -> content
        is ResText -> context.getString(resId, *args.toTypedArray())
    }
}

这种实现下ViewModel的所有逻辑都可以做纯JVM单元测试,不需要依赖Android环境,同时也避免了格式化逻辑散落在View层各处的问题。


内容的提问来源于stack exchange,提问作者Carlos Jiménez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.13 13:00:04