【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 处定义了初始的状态,也就是 loopreceiver 参数 S, 并从 2 处开始进入循环,也就是开始了我们状态的"自循环"。

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

除了状态本身,自循环扩展函数 loop 提供了两个用来干涉循环流程的函数 onEachonNext 来使得 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
    }
}

结尾

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

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

相关推荐
盛派网络小助手1 小时前
微信 SDK 更新 Sample,NCF 文档和模板更新,更多更新日志,欢迎解锁
开发语言·人工智能·后端·架构·c#
∝请叫*我简单先生2 小时前
java如何使用poi-tl在word模板里渲染多张图片
java·后端·poi-tl
zquwei3 小时前
SpringCloudGateway+Nacos注册与转发Netty+WebSocket
java·网络·分布式·后端·websocket·网络协议·spring
dessler3 小时前
Docker-run命令详细讲解
linux·运维·后端·docker
Q_19284999064 小时前
基于Spring Boot的九州美食城商户一体化系统
java·spring boot·后端
ZSYP-S4 小时前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
越甲八千4 小时前
重温设计模式--享元模式
设计模式·享元模式
Yuan_o_5 小时前
Linux 基本使用和程序部署
java·linux·运维·服务器·数据库·后端
程序员一诺5 小时前
【Python使用】嘿马python高级进阶全体系教程第10篇:静态Web服务器-返回固定页面数据,1. 开发自己的静态Web服务器【附代码文档】
后端·python