狀態機是古老的計算機理論,在遊戲開發、嵌入式開發、網絡協議等領域,獲得普遍地使用。java
狀態機:它是一個有向圖形,由一組節點和一組相應的轉移函數組成。狀態機經過響應一系列事件而「運行」。每一個事件都在屬於「當前」 節點的轉移函數的控制範圍內,其中函數的範圍是節點的一個子集。函數返回「下一個」(也許是同一個)節點。這些節點中至少有一個必須是終態。當到達終態, 狀態機中止。android
有限狀態機,(英語:Finite-state machine, FSM),又稱有限狀態自動機,簡稱狀態機,是表示有限個狀態以及在這些狀態之間的轉移和動做等行爲的數學模型。git
如下是對狀態機抽象定義github
肯定有限狀態自動機或肯定有限自動機(英語:deterministic finite automaton, DFA)是一個能實現狀態轉移的自動機對於一個給定的屬於該自動機的狀態和一個屬於該自動機字母表的字符,它都能根據事先給定的轉移函數轉移到下一個狀態(這個狀態能夠是先前那個狀態)。後端
DFA 是 FSM 的一種,與 DFA 對應的還有 NFA(非肯定性有限自動機)。數組
DFA 的特性:bash
之前我寫過的一篇文章《一個快速分析android app使用了哪些sdk的工具》 曾經使用過DFA。網絡
層次狀態機(英語:Hierarchical State Machine)是狀態機理論中的一種層次結構的模型,各個狀態按照樹狀層次結構組織起來,狀態圖是層次結構的,也就是說每一個狀態能夠擁有子狀態。app
當 FSM 狀態太多的時候,能夠將狀態分類,並抽離出來。同類型的狀態作爲一個狀態機,而後再作一個大的狀態機,來維護這些子狀態機。框架
github 地址:github.com/fengzhizi71…
用於保存管理 State 對象實例,表示 State 實例所處的環境。
interface StateContext {
fun getEvent(): BaseEvent
fun getSource(): BaseState
fun getTarget(): BaseState
fun getException(): Exception?
fun setException(exception: Exception)
fun getTransition(): Transition
}
複製代碼
構成狀態機的基本單位,狀態機在任何特定時間均可處於某一狀態。
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
}
複製代碼
從一個狀態切換到另外一個狀態。
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 的列表。
可以監聽 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)
}
複製代碼
只能監聽 Transition 發生的變化,也就是進入 State、離開 State。
interface TransitionCallback {
fun enteringState( stateMachine: StateMachine, currentState: BaseState, transition: Transition, targetState: BaseState )
fun enteredState( stateMachine: StateMachine, previousState: BaseState, transition: Transition, currentState: BaseState )
}
複製代碼
定義了狀態內部執行的 action、Transition 執行的 action、以及是否執行 Transition 的斷言。
typealias StateAction = (State) -> Unit
typealias TransitionAction = (Transition) -> Unit
typealias Guard = ()->Boolean
複製代碼
經過對 StateMachine 增長擴展屬性 enterTransitionObservable、exitTransitionObservable 能夠監聽到進入 State、離開 State 發生的變化。
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 }
複製代碼
舉一個簡單的例子,用 FSM 來模擬用戶從初始狀態,到吃飯的狀態,最後到看電視的狀態。
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
複製代碼
之因此開發一款 FSM 框架,主要是爲了重構公司的項目。趁疫情期間正好把之前的項目捋一捋。目前打算將這個 FSM 應用在咱們的移動端和後端的項目上。
參考資料: