Skip to content

A lightweight finite state machine featuring transition remarks and dry-run capability, inspired by Tinder's StateMachine.

Notifications You must be signed in to change notification settings

hwding/state-machine

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

State Machine

A lightweight finite state machine featuring transition remarks and dry-run capability, inspired by Tinder's StateMachine.

Quick Start

Define and do state transition on an FSM for H2O, you can get full codes here.

State, Event and SideEffect

sealed class State {
    object Solid : State()
    object Liquid : State()
    object Vapour : State()

    override fun toString(): String {
        return this.javaClass.simpleName
    }
}

sealed class Event {
    object Melt : Event()
    object Freeze : Event()
    object Vaporize : Event()
    object Condense : Event()
    object Sublimate : Event()
    object Deposit : Event()

    override fun toString(): String {
        return this.javaClass.simpleName
    }
}

sealed class SideEffect {

    // absorb heat
    object Endothermic : SideEffect()

    // release heat
    object Exothermic : SideEffect()

    override fun toString(): String {
        return this.javaClass.simpleName
    }
}

Graph and StateMachine

StateMachine.create {
    initialState(State.Liquid)

    state<State.Liquid> {
        on<Event.Freeze> {
            transitionTo(state = State.Solid, sideEffect = SideEffect.Exothermic)
        }

        on<Event.Vaporize> {
            transitionTo(state = State.Vapour, sideEffect = SideEffect.Endothermic)
        }
    }

    state<State.Solid> {
        on<Event.Melt> {
            transitionTo(state = State.Liquid, sideEffect = SideEffect.Endothermic)
        }

        on<Event.Sublimate> {
            transitionTo(state = State.Vapour, sideEffect = SideEffect.Endothermic)
        }
    }

    state<State.Vapour> {
        on<Event.Condense> {
            transitionTo(state = State.Liquid, sideEffect = SideEffect.Exothermic)
        }

        on<Event.Deposit> {
            transitionTo(state = State.Solid, sideEffect = SideEffect.Exothermic)
        }
    }

    onTransition {
        val validTransition =
            it as? StateMachine.Transition.Valid ?: throw kotlin.Exception("Invalid transition")

        println(
            "transition" +
                    " from state ${validTransition.fromState}" +
                    " to state ${validTransition.toState}" +
                    " on ${validTransition.event}" +
                    " with side effect ${validTransition.sideEffect}"
        )

        when (validTransition.sideEffect) {
            SideEffect.Exothermic -> {
                println("Release heat 🔥🔥🔥 " + (validTransition.eventRemark ?: ""))
            }

            SideEffect.Endothermic -> {
                println("Absorb heat ❄️❄️❄️ " + (validTransition.eventRemark ?: ""))
            }

            else -> {
                println("unexpected side effect ${validTransition.sideEffect}")
            }
        }
    }
}

Transition

class StateMachineTest {

    @Test
    fun test() {
        val fsm = h2oFsm()

        // dry run transition valid: false, current state: Liquid
        val dryRunTransition1 = fsm.dryRunTransition(event = Event.Condense, eventRemark = "it shouldn't happen!")
        println("dry run transition valid: ${dryRunTransition1 is StateMachine.Transition.Valid}, current state: ${fsm.state}")
        println()

        // dry run transition valid: true, current state: Liquid
        val dryRunTransition2 = fsm.dryRunTransition(event = Event.Freeze)
        println("dry run transition valid: ${dryRunTransition2 is StateMachine.Transition.Valid}, current state: ${fsm.state}")
        println()

        // transition exception: Invalid transition
        // transition valid: false, current state: Liquid
        val transition1Valid = try {
            val transition = fsm.transition(event = Event.Condense, eventRemark = "it shouldn't happen!")
            transition is StateMachine.Transition.Valid
        } catch (e: Exception) {
            println("transition exception: ${e.localizedMessage}")
            false
        }
        println("transition valid: ${transition1Valid}, current state: ${fsm.state}")
        println()

        // transition from state Liquid to state Solid on Freeze with side effect Exothermic
        // Release heat 🔥🔥🔥 for that it's -2 degree Celsius outside!
        // transition valid: true, current state: Solid
        val transition2Valid = try {
            val transition = fsm.transition(
                event = Event.Freeze,
                eventRemark = "for that it's -2 degree Celsius outside!"
            )

            transition is StateMachine.Transition.Valid
        } catch (e: Exception) {
            println("transition exception: ${e.localizedMessage}")
            false
        }
        println("transition valid: ${transition2Valid}, current state: ${fsm.state}")
        println()

        // transition from state Solid to state Vapour on Sublimate with side effect Endothermic
        // Absorb heat ❄️❄️❄️ for we hava a frozen wet cloth dries in cold winter air.
        // transition valid: true, current state: Vapour
        val transition3Valid = try {
            val transition = fsm.transition(
                event = Event.Sublimate,
                eventRemark = "for we hava a frozen wet cloth dries in cold winter air."
            )
            transition is StateMachine.Transition.Valid
        } catch (e: Exception) {
            println("transition exception: ${e.localizedMessage}")
            false
        }
        println("transition valid: ${transition3Valid}, current state: ${fsm.state}")
        println()
    }
}

About

A lightweight finite state machine featuring transition remarks and dry-run capability, inspired by Tinder's StateMachine.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages