扩展函数与高阶函数在架构模式中的应用

今天我们来聊聊架构模式。说实话,很多Android开发者对MVP、MVVM、MVI这些概念并不陌生,但真正用起来总觉得代码量太大、模板代码太多。我个人习惯用扩展函数和高阶函数来简化这些架构模式,效果还不错。

你想想看,架构模式的核心是什么?说白了就是分离关注点。但分离带来的代价就是多了很多接口、回调、模板代码。扩展函数和高阶函数正好能帮我们解决这个问题。

MVP的扩展函数简化

MVP模式里最烦人的是什么?我觉得是View和Presenter之间的接口定义。每个页面都要写一个View接口,然后Presenter持有这个接口的引用。代码量翻倍不说,维护起来也麻烦。

我在项目中遇到过这样一个场景:一个列表页面,需要展示数据、显示加载状态、处理错误。如果用传统MVP,至少要写三个接口方法。但用扩展函数,我们可以这样简化:

// 传统MVP的View接口
interface UserListView {
    fun showUsers(users: List<User>)
    fun showLoading()
    fun hideLoading()
    fun showError(message: String)
}

// 用扩展函数简化
fun <T> BaseView.showData(
    data: T?,
    loading: Boolean = false,
    error: String? = null,
    onSuccess: (T) -> Unit,
    onError: ((String) -> Unit)? = null
) {
    if (loading) {
        showLoading()
        return
    }
    error?.let {
        onError?.invoke(it) ?: showError(it)
        return
    }
    data?.let { onSuccess(it) }
}

核心思路:把View接口的方法封装成扩展函数,用高阶函数替代接口回调。这样Presenter调用时只需要传一个lambda,代码量减少一半。

嗯,这里要注意一点:扩展函数不能覆盖已有的方法。所以如果你的View接口里已经有showLoading()方法,扩展函数里就不能再定义同名的了。我一般会在基类里统一处理这些通用逻辑。

MVVM的扩展函数

MVVM模式里,LiveData或StateFlow的使用频率很高。但每次都要写observe、collect,也挺烦的。我习惯写一些扩展函数来简化这些操作。

// LiveData的扩展函数
fun <T> LiveData<T>.observeOnce(
    owner: LifecycleOwner,
    action: (T) -> Unit
) {
    observe(owner, object : Observer<T> {
        override fun onChanged(value: T) {
            action(value)
            removeObserver(this)
        }
    })
}

// StateFlow的扩展函数
fun <T> StateFlow<T>.collectIn(
    scope: CoroutineScope,
    action: (T) -> Unit
) {
    scope.launch {
        collect { action(it) }
    }
}

// 更实用的:自动处理Loading状态
fun <T> LiveData<UiState<T>>.observeState(
    owner: LifecycleOwner,
    onSuccess: (T) -> Unit,
    onLoading: (() -> Unit)? = null,
    onError: ((String) -> Unit)? = null
) {
    observe(owner) { state ->
        when (state) {
            is UiState.Loading -> onLoading?.invoke()
            is UiState.Success -> onSuccess(state.data)
            is UiState.Error -> onError?.invoke(state.message)
        }
    }
}

我的经验:在MVVM里,我习惯把UI状态封装成一个sealed class,然后用扩展函数统一处理。这样ViewModel里只需要发射状态,View层只需要观察状态,中间的逻辑全部由扩展函数搞定。

为什么会这样设计?因为MVVM的核心是数据驱动,ViewModel不应该知道View的存在。用扩展函数处理状态分发,既保持了ViewModel的纯净,又简化了View层的代码。

MVI的DSL构建

MVI模式是我最近比较喜欢的架构。它的核心是单向数据流:View发送Intent,ViewModel处理Intent并更新State,View根据State渲染UI。但MVI的代码量也不小,尤其是定义Intent和State的时候。

用DSL来构建MVI的组件,效果出奇的好。我曾经在一个中型项目里尝试过,代码量减少了大概40%。

// MVI的DSL构建
class MviViewModel<S : Any, I : Any>(
    initialState: S,
    private val reducer: (S, I) -> S
) : ViewModel() {

    private val _state = MutableStateFlow(initialState)
    val state: StateFlow<S> = _state.asStateFlow()

    fun dispatch(intent: I) {
        _state.update { reducer(it, intent) }
    }
}

// DSL构建器
fun <S : Any, I : Any> mviViewModel(
    initialState: S,
    reducer: (S, I) -> S
): MviViewModel<S, I> = MviViewModel(initialState, reducer)

// 使用示例
class CounterViewModel : ViewModel() {

    // 定义State
    data class CounterState(
        val count: Int = 0,
        val isLoading: Boolean = false
    )

    // 定义Intent
    sealed class CounterIntent {
        object Increment : CounterIntent()
        object Decrement : CounterIntent()
        object Reset : CounterIntent()
    }

    // 用DSL构建ViewModel
    val viewModel = mviViewModel(CounterState()) { state, intent ->
        when (intent) {
            is CounterIntent.Increment -> state.copy(count = state.count + 1)
            is CounterIntent.Decrement -> state.copy(count = state.count - 1)
            is CounterIntent.Reset -> state.copy(count = 0)
        }
    }
}

避坑指南:我曾经在MVI里把reducer写得太复杂,一个reducer里处理了七八种Intent。结果代码变得很难维护。后来我学乖了,每个reducer只处理一种Intent,或者把复杂的逻辑拆成多个小reducer。

DSL的好处是什么?你想想看,它把ViewModel的创建过程封装成了一个声明式的API。你只需要告诉它初始状态和状态转换逻辑,剩下的它帮你搞定。这不就是DSL的魅力吗?

三种架构的对比

架构模式 扩展函数/高阶函数的作用 典型应用场景 我的评价
MVP 简化View接口,用lambda替代回调 列表页面、表单页面 适合中小型项目,扩展函数能减少模板代码
MVVM 封装LiveData/StateFlow的观察逻辑 数据驱动页面、复杂状态管理 最常用,扩展函数让数据流更清晰
MVI DSL构建ViewModel和Reducer 状态复杂、需要可追溯性的场景 代码量最少,但学习曲线稍陡

说实话,这三种架构没有绝对的好坏。我个人的选择标准是:项目规模小用MVP,中等规模用MVVM,状态特别复杂用MVI。但不管用哪种,扩展函数和高阶函数都能帮我们写出更简洁的代码。

扩展函数与高阶函数在架构模式中的应用 MVP MVVM MVI 扩展函数简化 用lambda替代View接口回调 减少模板代码 扩展函数封装 封装LiveData/StateFlow观察 统一处理Loading/Success/Error DSL构建 声明式API构建ViewModel Reducer纯函数管理状态 核心思想:用扩展函数消除模板代码,用高阶函数替代接口回调 让架构模式更专注于业务逻辑,而不是样板代码

最后说一句,扩展函数和高阶函数只是工具,不是银弹。用得好能让代码更优雅,用得不好反而会增加复杂度。我的建议是:先从简单的场景开始,比如封装一个Loading状态的处理,慢慢扩展到整个架构层。

总结一下

  • MVP:用扩展函数简化View接口,用高阶函数替代回调
  • MVVM:用扩展函数封装数据观察逻辑,统一处理UI状态
  • MVI:用DSL构建ViewModel和Reducer,让状态管理更声明式

嗯,今天就聊到这里。这些技巧我在实际项目中都用过,效果确实不错。如果你在项目中遇到架构相关的代码冗余问题,不妨试试用扩展函数和高阶函数来优化一下。

公众号:蓝海资料掘金营,微信deep3321