Конечный автомат, разработанный на основе функций Kotlin

Государственный аппарат Kotlin

woman-standing-beside-flowers-1853424.jpg

1. Государственный автомат

Конечный автомат — это древняя компьютерная теория, которая широко используется в разработке игр, встраиваемых систем, сетевых протоколов и других областях.

State Machine: это ориентированный граф, состоящий из набора узлов и набора соответствующих функций перехода. Конечный автомат «запускается» в ответ на серию событий. Каждое событие находится в пределах области управления функции перехода, принадлежащей «текущему» узлу, где областью действия функции является подмножество узла. Функция возвращает «следующий» (возможно, тот же) узел. По крайней мере один из этих узлов должен быть в конечном состоянии. При достижении конечного состояния конечный автомат останавливается.

2. Обычно используемая классификация конечных автоматов

FSM

Конечный автомат (английский: Finite-state machine, FSM), также известный как конечный автомат или, для краткости, конечный автомат, представляет собой математическую модель, которая представляет конечное число состояний и поведений, таких как переходы и действия между этими состояниями. состояния.

Ниже приводится абстрактное определение конечного автомата.

  • Состояние: основная единица, составляющая конечный автомат. Конечный автомат может находиться в состоянии в любой момент времени. С точки зрения жизненного цикла есть начальное состояние, конечное состояние, состояние приостановки (состояние приостановки).
  • Событие: действие события, вызвавшее переход.
  • Переходы: отношения направленного перехода между двумя состояниями.Текущее состояние переходит из A в B после того, как конечный автомат реагирует на событие определенного типа. Различные абстрактные реализации стандартного преобразования, преобразования выбора и преобразования подпроцесса.
  • Действия: определенные действия, которые необходимо выполнить при выполнении определенного перехода.
  • Охранники (детектор): причиной появления детектора является переход из состояния в определенное состояние после выполнения операции перехода, чтобы определить, соответствует ли результат определенным условиям.
  • Перехватчик: Отслеживайте и перехватывайте текущее состояние до и после изменения.

状态表.jpg

DFA

Детерминированный конечный автомат или детерминированный конечный автомат (DFA) — это автомат, который может осуществлять переходы между состояниями для заданного состояния, принадлежащего автомату, и символа, принадлежащего автоматному алфавиту. Он может переходить в следующее состояние (это состояние может быть предыдущее состояние) в соответствии с заранее заданной функцией перехода.

DFA — это разновидность FSM, а NFA (недетерминированный конечный автомат) соответствует DFA.

Особенности ДФА:

  • Отсутствие конфликта: состояние не может иметь несколько правил для одного и того же входа, то есть каждый вход может иметь только одно правило перехода;
  • Никаких упущений: каждое состояние должно иметь хотя бы одно правило для каждого возможного входного символа.

Статья, которую я написал раньше«Инструмент для быстрого анализа того, какие SDK используются приложениями для Android»Раньше использовали DFA.

HSM

Hierarchical State Machine (англ. Hierarchical State Machine) — модель иерархической структуры в теории конечных автоматов. Каждое состояние организовано в соответствии с древовидной иерархией. Диаграмма состояний иерархична, то есть каждое состояние может иметь подсистемы. государства.

Когда состояний FSM слишком много, состояния можно классифицировать и извлечь. Тот же тип состояния используется в качестве конечного автомата, а затем создается большой конечный автомат для поддержки этих подмашин.

3. FSM, разработанный Kotlin

адрес гитхаба:GitHub.com/Zonda71…

StateContext

Используется для сохранения и управления экземплярами объекта State, представляющего среду, в которой находится экземпляр State.

interface StateContext {

    fun getEvent(): BaseEvent

    fun getSource(): BaseState

    fun getTarget(): BaseState

    fun getException(): Exception?

    fun setException(exception: Exception)

    fun getTransition(): Transition
}

State

Базовая единица, составляющая конечный автомат, который может находиться в определенном состоянии в любой момент времени.

class State(val name: BaseState) {

    private val transitions = hashMapOf<BaseEvent, Transition>() // 存储当前 State 相关的所有 Transition
    private val stateActions = mutableListOf<StateAction>()  // 当前 State 相关的所有 Action

    /**
     * 当一个 Event 被状态机系统分发的时候,状态机用 Action 来进行响应
     * 状态转换可以使用 F(S, E) -> (A, S’) 表示
     *
     * @param event: 触发事件
     * @param targetState: 下一个状态
     * @param guard: 断言接口,为了转换操作执行后检测结果是否满足特定条件从一个状态切换到某一个状态
     * @param init
     */
    fun transition(event: BaseEvent, targetState: BaseState, guard: Guard?=null, init: Transition.() -> Unit):State {
        val transition = Transition(event, this.name, targetState, guard)
        transition.init()

        if (transitions.containsKey(event)) { // 同一个 Event 不能对应多个 Transition,即 State 只能通过一个 Event 然后 Transition 到另一个 State
            throw StateMachineException("Adding multiple transitions for the same event is invalid")
        }

        transitions[event] = transition
        return this
    }

    /**
     * State 执行的 Action
     */
    fun action(action: StateAction) {
        stateActions.add(action)
    }

    /**
     * 进入 State 并执行所有的 Action
     */
    fun enter() {
        stateActions.forEach {
            it.invoke(this)
        }
    }

    /**
     * 通过 Event 获取 Transition
     */
    fun getTransitionForEvent(event: BaseEvent): Transition = transitions[event]?:throw IllegalStateException("Event $event isn't registered with state ${this.name}")

    override fun toString(): String = name.javaClass.simpleName
}

Transition

Переключение из одного состояния в другое.

class Transition(private val event: BaseEvent, private val sourceState: BaseState, private val targetState: BaseState, private var guard:Guard?= null) {

    private val actions = mutableListOf<TransitionAction>()

    /**
     * 是否转换
     * @param context
     */
    fun transit(context: StateContext): Boolean {
        executeTransitionActions(context)
        return context.getException() == null
    }

    /**
     * 执行 Transition 的 Action
     */
    private fun executeTransitionActions(context: StateContext) {

        actions.forEach {
            try {
                it.invoke(this)
            } catch (e:Exception) {
                context.setException(e)
                return
            }
        }
    }

    /**
     * 添加一个 action,在状态转换时执行(时间点是在状态转换之前)
     */
    fun action(action: TransitionAction) {
        actions.add(action)
    }


    /**
     * 转换状态
     */
    fun applyTransition(getNextState: (BaseState) -> State): State = getNextState(targetState)

    /**
     * 设置检测条件,判断是否满足状态转换的条件,满足则执行状态转换
     */
    fun guard(guard: Guard) {
        this.guard = guard
    }

    fun getGuard():Guard? = guard

    fun getSourceState(): BaseState = sourceState

    fun getTargetState(): BaseState = targetState

    override fun toString(): String = "${sourceState.javaClass.simpleName} transition to ${targetState.javaClass.simpleName} on ${event.javaClass.simpleName}"
}

Реализация конечного автомата

class StateMachine private constructor(private val initialState: BaseState) {

    private lateinit var currentState: State    // 当前状态
    private val states = mutableListOf<State>() // 状态列表
    private val initialized = AtomicBoolean(false) // 是否初始化
    private var globalInterceptor: GlobalInterceptor?=null
    private val transitionCallbacks: MutableList<TransitionCallback> = mutableListOf()

    /**
     * 设置状态机全局的拦截器,使用时必须要在 initialize() 之前
     * @param event: 状态机全局的拦截器
     */
    fun interceptor(globalInterceptor: GlobalInterceptor):StateMachine {
        this.globalInterceptor = globalInterceptor
        return this
    }

    /**
     * 初始化状态机,并进入初始化状态
     */
    fun initialize() {
        if(initialized.compareAndSet(false, true)){
            currentState = getState(initialState)
            globalInterceptor?.stateEntered(currentState)
            currentState.enter()
        }
    }

    /**
     * 向状态机添加 State
     */
    fun state(stateName: BaseState, init: State.() -> Unit):StateMachine {
        val state = State(stateName)
        state.init()
        states.add(state)
        return this
    }

    /**
     * 通过状态名称获取状态
     */
    private fun getState(stateType: BaseState): State = states.firstOrNull { stateType.javaClass == it.name.javaClass } ?: throw NoSuchElementException(stateType.javaClass.canonicalName)

    /**
     * 向状态机发送 Event,执行状态转换
     */
    @Synchronized
    fun sendEvent(e: BaseEvent) {
        try {
            val transition = currentState.getTransitionForEvent(e)

            globalInterceptor?.transitionStarted(transition)

            val stateContext: StateContext = DefaultStateContext(e, transition, transition.getSourceState(), transition.getTargetState())

            //状态转换之前执行的 action(Transition 内部的 action), action执行失败表示不接受事件,返回false
            val accept = transition.transit(stateContext)

            if (!accept) {
                //状态机发生异常
                globalInterceptor?.stateMachineError(this, StateMachineException("状态转换失败,source ${currentState.name} -> target ${transition.getTargetState()} Event ${e}"))
                return
            }

            val guard = transition.getGuard()?.invoke()?:true

            if (guard) {
                val state = transition.applyTransition { getState(stateContext.getTarget()) }

                val callbacks = transitionCallbacks.toList()

                globalInterceptor?.apply {
                    stateContext(stateContext)
                    transition(transition)
                    stateExited(currentState)
                }

                callbacks.forEach { callback ->
                    callback.enteringState(this, stateContext.getSource(), transition, stateContext.getTarget())
                }

                state.enter()

                callbacks.forEach { callback ->
                    callback.enteredState(this, stateContext.getSource(), transition, stateContext.getTarget())
                }

                globalInterceptor?.apply {
                    stateEntered(state)
                    stateChanged(currentState,state)
                    transitionEnded(transition)
                }

                currentState = state
            } else {
                println("$transition 失败")

                globalInterceptor?.stateMachineError(this, StateMachineException("状态转换时 guard [${guard}], 状态 [${currentState.name}],事件 [${e.javaClass.simpleName}]"))
            }
        } catch (exception:Exception) {

            globalInterceptor?.stateMachineError(this, StateMachineException("This state [${this.currentState.name}] doesn't support transition on ${e.javaClass.simpleName}"))
        }
    }

    @Synchronized
    fun getCurrentState(): BaseState = this.currentState.name

    /**
     * 注册 TransitionCallback
     */
    fun registerCallback(transitionCallback: TransitionCallback) = transitionCallbacks.add(transitionCallback)

    /**
     * 取消 TransitionCallback
     */
    fun unregisterCallback(transitionCallback: TransitionCallback) = transitionCallbacks.remove(transitionCallback)

    companion object {

        fun buildStateMachine(initialStateName: BaseState, init: StateMachine.() -> Unit): StateMachine {
            val stateMachine = StateMachine(initialStateName)
            stateMachine.init()
            return stateMachine
        }
    }
}

В StateMachine содержит глобальный GlobalInterceptor и список TransitionCallback.

GlobalInterceptor

Возможность прослушивания State, Transition, StateContext и исключений.

interface GlobalInterceptor {

    /**
     * 进入某个 State
     */
    fun stateEntered(state: State)

    /**
     * 离开某个 State
     */
    fun stateExited(state: State)

    /**
     * State 发生改变
     * @param from: 当前状态
     * @param to:   下一个状态
     */
    fun stateChanged(from: State, to: State)

    /**
     * 触发 Transition
     */
    fun transition(transition: Transition)

    /**
     * 准备开始 Transition
     */
    fun transitionStarted(transition: Transition)

    /**
     * Transition 结束
     */
    fun transitionEnded(transition: Transition)

    /**
     * 状态机异常的回调
     */
    fun stateMachineError(stateMachine: StateMachine, exception: Exception)

    /**
     * 监听状态机上下文
     */
    fun stateContext(stateContext: StateContext)
}

TransitionCallback

Вы можете только следить за изменениями в Transition, то есть входить и выходить из State.

interface TransitionCallback {

    fun enteringState(
        stateMachine: StateMachine,
        currentState: BaseState,
        transition: Transition,
        targetState: BaseState
    )

    fun enteredState(
        stateMachine: StateMachine,
        previousState: BaseState,
        transition: Transition,
        currentState: BaseState
    )
}

TypeAliases

Определяет действия, выполняемые внутри состояния, действия, выполняемые переходом, и утверждение о том, следует ли выполнять переход.

typealias StateAction = (State) -> Unit

typealias TransitionAction = (Transition) -> Unit

typealias Guard = ()->Boolean

Поддержка RxJava 2

Добавляя расширенные атрибуты enterTransitionObservable и exitTransitionObservable в StateMachine, вы можете отслеживать изменения, происходящие при входе и выходе из состояния.

val StateMachine.stateObservable: Observable<TransitionEvent>
    get() = Observable.create { emitter ->
        val rxCallback = RxStateCallback(emitter)
        registerCallback(rxCallback)
        emitter.setCancellable {
            unregisterCallback(rxCallback)
        }
    }

val StateMachine.enterTransitionObservable: Observable<TransitionEvent.EnterTransition>
    get() = stateObservable
        .filter { event -> event is TransitionEvent.EnterTransition }
        .map { event -> event as TransitionEvent.EnterTransition }

val StateMachine.exitTransitionObservable: Observable<TransitionEvent.ExitTransition>
    get() = stateObservable
        .filter { event -> event is TransitionEvent.ExitTransition }
        .map { event -> event as TransitionEvent.ExitTransition }

4. Применение

Чтобы привести простой пример, используйте FSM для имитации пользователя от начального состояния до состояния еды и, наконец, до состояния просмотра телевизора.

demo.png

fun main() {

    val sm = StateMachine.buildStateMachine(Initial()) {

        state(Initial()) {
            action {
                println("Entered [$it] State")
            }

            transition(Cook(), Eat()) {
                action {
                    println("Action: Wash Vegetables")
                }

                action {
                    println("Action: Cook")
                }
            }
        }

        state(Eat()) {

            action {
                println("Entered [$it] State")
            }

            transition(WashDishes(), WatchTV()) {

                action {
                    println("Action: Wash Dishes")
                }

                action {
                    println("Action: Turn on the TV")
                }
            }
        }

        state(WatchTV()) {

            action {
                println("Entered [$it] State")
            }
        }
    }

    sm.initialize()
    sm.sendEvent(Cook())
    sm.sendEvent(WashDishes())
}

Результаты:

Entered [Initial] State
Action: Wash Vegetables
Action: Cook
Entered [Eat] State
Action: Wash Dishes
Action: Turn on the TV
Entered [WatchTV] State

V. Резюме

Основной причиной разработки FSM-фреймворка является реструктуризация проектов компании. Воспользуйтесь эпидемией, чтобы воспользоваться предыдущими проектами. В настоящее время мы планируем применить этот FSM к нашим мобильным и серверным проектам.

Использованная литература:

  1. машинное мышление
  2. обзор co.stack Exchange.com/questions/1…
  3. Компьютерные вычисления (1) - Конечные автоматы