【KTips】在Kotlin中实现一个十分简单的自循环状态机

在平日的编码生活中,你有没有遇到过需要通过状态机来实现的逻辑呢?一个状态的轮转、事件订阅的通讯处理等。 状态机的实现方式千变万化,这里我会为你介绍一个简单的自循环状态机实现。

简单介绍

在开始之前,我先说明一下这个所谓的 "自循环状态机" ,避免歧义。这个词儿是我自己编出来的, 意为这个状态机中的 动作 与 变换 均由状态本身来完成。

用大家都喜欢的大白话来讲,就是 "状态" 是一个 class,它自己包括了它的 "行为" ------ 也就是一个可执行方法, 然后这个方法的返回值便是下一个将被 "变换" 的 "状态" 。

文字表述还是缺乏力量,接下来便用代码来为你展示。

实现

类型定义

让我们来定义状态机中不断轮转的状态。

Kotlin 复制代码
public abstract class State<S : State<S>> {
    
    /**
     * 行为.
     * @return 下一个状态,或结束循环。 
     */
    public abstract operator fun invoke(): S?
}

那么最基础的抽象定义便结束了。很简单不是吗?

!info 根据业务需求,invoke 也可以适当地调整,比如让它变为 suspend 可挂起的。

自循环扩展

定义完了非常简单的状态 类型 State,接下来就是实现它的 "自循环"。 State 的自循环其实很简单:提供一个初始状态,然后不断地循环, 用 invoke 的结果来代替当前的状态,直到循环结束。

但是在每一个使用状态机的地方都去写这么一些逻辑还是会有些啰嗦, 此时扩展函数便派上了用场。

我们先来看代码:

Kotlin 复制代码
public suspend inline fun <S : State<S>> S.loop(
    onEach: (S) -> Boolean = { true },
    onNext: (S?) -> S? = { it }
): S {
    var state = this // 1
    while (onEach(state)) { // 2
        state().let(onNext)?.also { state = it } ?: return state // 3
    }

    return state
}

在上面的代码中,我们在 1 处定义了初始的状态,也就是 loop 的 receiver 参数 S, 并从 2 处开始进入循环,也就是开始了我们状态的"自循环"。

每一次循环我们都会执行当前状态 state 的行为逻辑 invoke,并将它的返回结果设置为当前状态, 如此循环往复,直到循环终止,或者行为逻辑 invoke 返回了 null,并最终向 loop 返回最后一个被执行的状态。

除了状态本身,自循环扩展函数 loop 提供了两个用来干涉循环流程的函数 onEach 和 onNext 来使得 loop 更加灵活, 而得益于 Kotlin 的内联函数,它们几乎不会带来什么副作用。

简单用法

纸上得来终觉浅,我们用一个简单的例子来看看这个状态机是如何使用的。 我们假设有一个订阅事件的逻辑如下:

flowchart LR 开始 --> 接收事件 接收事件 --> |获取10次|接收事件 接收事件 --> 结束

那么用代码来实现的话大概就可以是这个样子:

Kotlin 复制代码
fun main() {
    Start.loop()
}

/** 三个状态的统一类型 */
sealed class MyState : State<MyState>()

/** 代表开始。 */
data object Start : MyState() {
    override fun invoke(): MyState {
        // 进入 '接收事件' 阶段,此处假设给它分配了一个id
        return Receive(Random.nextLong())
    }
}

/** 代表接收事件。有一个计数器,到达10后结束 */
data class Receive(val id: Long) : MyState() {
    var times = 0
    override fun invoke(): MyState {
        // 达到10次以上,结束此状态循环,进入结束状态
        if (times >= 10) {
            return Done
        }
        println("times = $times")

        // 递增计数,并返回自身,也就是继续保持 Receive 状态
        times++
        return this
    }
}

/** 代表结束。 */
data object Done : MyState() {
    override fun invoke(): MyState? {
        // 结束状态可以再做一些什么操作,然后返回 null 终止循环
        println("Done!")
        return null
    }
}

结尾

以上就是一个简单的自循环状态机的实现啦!怎么样,是不是很简单呢?

如果有帮助到你,我的荣幸~

相关推荐
花间相见2 分钟前
【计算基础|MySQL18】Buffer Pool 与内存管理
后端·sql·mysql
花间相见1 小时前
【计算基础|Redis01】【上手】安装连接、配置与通用命令
数据库·后端·面试
我命由我123451 小时前
6-3.Python 异常处理 - 异常处理进阶(else 块与 finally 块、异常传递、自定义异常)
开发语言·前端·数据库·后端·python·学习·java-ee
深入云栈1 小时前
Netty 4.2.x 源码深度解析 (二十一):Pipeline 事件传播 —— Inbound 与 Outbound 的双向流转
java·后端·架构
卷无止境2 小时前
长篇写作 Skill 横向盘点:从单文件指令到多智能体流水线
后端·python
斯维赤2 小时前
微信步数的10点定时推送,看似简单,实则暗藏玄机
java·后端
RobinDevNotes2 小时前
crontab 管不住时,该上 Airflow 了
后端
系统集成-鹰翔2 小时前
ERP接口月度统计少了零单月份:SQLite分组与最近三个月均值排查
javascript·数据库·经验分享·后端
mONESY2 小时前
从线性 RAG 到 Agentic RAG|基于 LangGraph 实现自主决策 RAG 闭环
javascript·面试
我的xiaodoujiao3 小时前
Django 权限系统项目实战详细图文教程 3--系统登录功能实现
后端·python·django