26、架构模式:MVVM与Compose、MVI模式、单向数据流实践、Clean Architecture
聊到架构模式,很多新手会觉得这是“大项目才需要的东西”。
其实不然。我见过太多小项目,一开始图省事,所有逻辑全塞在Composable里,结果三个月后连自己都看不懂。
嗯,今天我们就来把这几个模式彻底讲透。
一、MVVM + Jetpack Compose:官方推荐的标准姿势
MVVM(Model-View-ViewModel)是Google官方推荐的架构。
在Compose里,它的核心思路就一句话:UI不存数据,数据驱动UI。
我个人习惯把ViewModel当作“状态管家”。
它持有StateFlow或MutableState,Composable只负责订阅和渲染。
// ViewModel 层
class CounterViewModel : ViewModel() {
private val _count = MutableStateFlow(0)
val count: StateFlow<Int> = _count.asStateFlow()
fun increment() {
_count.value += 1
}
}
// Composable 层
@Composable
fun CounterScreen(viewModel: CounterViewModel = viewModel()) {
val count by viewModel.count.collectAsState()
Button(onClick = { viewModel.increment() }) {
Text("点击了 $count 次")
}
}
二、MVI模式:让状态变化可追溯
MVI(Model-View-Intent)是MVVM的升级版。
它强调单向数据流和不可变状态。
说白了,MVI把用户操作抽象成“意图(Intent)”,所有状态变化都通过Reducer处理。
这样做的好处是:每个状态变化都有迹可循,调试起来特别爽。
// 定义状态
data class CounterState(val count: Int = 0, val isLoading: Boolean = false)
// 定义意图
sealed class CounterIntent {
object Increment : CounterIntent()
object Decrement : CounterIntent()
object Reset : CounterIntent()
}
// ViewModel 实现
class CounterViewModel : ViewModel() {
private val _state = MutableStateFlow(CounterState())
val state: StateFlow<CounterState> = _state.asStateFlow()
fun processIntent(intent: CounterIntent) {
_state.update { currentState ->
when (intent) {
is CounterIntent.Increment -> currentState.copy(count = currentState.count + 1)
is CounterIntent.Decrement -> currentState.copy(count = currentState.count - 1)
is CounterIntent.Reset -> currentState.copy(count = 0)
}
}
}
}
三、单向数据流:Compose的底层哲学
Compose本身就是单向数据流的产物。
父组件通过参数传数据给子组件,子组件通过回调通知父组件。
你想想看,如果子组件能直接修改父组件的状态,那调试起来得多痛苦?
单向数据流的核心原则:数据向下流动,事件向上传递。
@Composable
fun Parent() {
var text by remember { mutableStateOf("") }
Child(text = text, onTextChange = { text = it })
}
@Composable
fun Child(text: String, onTextChange: (String) -> Unit) {
TextField(value = text, onValueChange = onTextChange)
}
四、Clean Architecture:分层解耦的艺术
Clean Architecture把代码分成三层:
数据层(Data)、领域层(Domain)、展示层(Presentation)。
我刚开始接触时觉得太啰嗦,一个小功能要写三个类。
但后来项目规模大了,才发现分层的好处:替换数据库、切换网络库,只需要改数据层,其他层完全不动。
| 层级 | 职责 | 典型内容 |
|---|---|---|
| Data | 数据来源 | Repository实现、Room DAO、Retrofit API |
| Domain | 业务逻辑 | UseCase、Entity、Repository接口 |
| Presentation | UI展示 | ViewModel、Composable、State |
// Domain 层:定义接口
interface UserRepository {
suspend fun getUser(id: String): User
}
// Data 层:实现接口
class UserRepositoryImpl(private val api: UserApi) : UserRepository {
override suspend fun getUser(id: String): User {
return api.fetchUser(id).toDomain()
}
}
// Presentation 层:使用
class UserViewModel(private val repository: UserRepository) : ViewModel() {
fun loadUser(id: String) {
viewModelScope.launch {
val user = repository.getUser(id)
_state.update { it.copy(user = user) }
}
}
}
五、架构模式对比与选择
我整理了一张对比表,方便你根据项目情况选择:
| 模式 | 适用场景 | 复杂度 | 可测试性 |
|---|---|---|---|
| MVVM | 中小型项目、快速迭代 | 低 | 中 |
| MVI | 复杂交互、需要状态回溯 | 中 | 高 |
| Clean Architecture | 大型项目、多团队协作 | 高 | 极高 |
我个人习惯:
小工具用MVVM,业务复杂的模块用MVI,整个项目用Clean Architecture组织。
三者并不冲突,可以混合使用。
六、架构核心逻辑图
下面这张SVG图,展示了MVVM、MVI、Clean Architecture之间的关系:
七、总结
架构模式没有银弹。
MVVM适合快速开发,MVI适合复杂状态管理,Clean Architecture适合大型项目。
我建议你从MVVM入手,逐步引入MVI的单向数据流思想,等团队成熟了再上Clean Architecture。
记住:架构是为了让代码更容易维护,而不是为了炫技。
选最适合你当前项目的模式,比选最流行的模式重要得多。
公众号:蓝海资料掘金营,微信deep3321