Skip to content

Instantly share code, notes, and snippets.

@matnogaj
Last active December 26, 2017 21:37
Show Gist options
  • Save matnogaj/b67c5922553884203a3e55df2af2fb22 to your computer and use it in GitHub Desktop.
Save matnogaj/b67c5922553884203a3e55df2af2fb22 to your computer and use it in GitHub Desktop.
State machine in Kotlin
class StateMachine(private val initialState: State) {
interface Event
interface State
class Edge(private val triggerEvent: Event, private val targetState: State) {
private val actionList = mutableListOf<(Edge) -> Unit>()
fun action(action: (Edge) -> Unit) {
actionList.add(action)
}
//Invoke when you go down the edge to another state
fun enterEdge(): State {
actionList.forEach { it(this) }
return targetState
}
fun canHandleEvent(event: Event): Boolean = (event == triggerEvent)
}
class StateWrapper(val state: State) {
private val edgeList = mutableListOf<Edge>()
fun edge(event: Event, targetState: State, init: Edge.() -> Unit = {}) {
edgeList.add(Edge(event, targetState).apply { init() })
}
private val stateEnterAction = mutableListOf<(State) -> Unit>()
//Add an action which will be called when the state is entered
fun action(action: (State) -> Unit) {
stateEnterAction.add(action)
}
fun enterState() {
stateEnterAction.forEach { it(this.state) }
}
//Get the appropriate Edge for the Event
fun getEdgeForEvent(event: Event): Edge? = edgeList.firstOrNull { it.canHandleEvent(event) }
}
companion object {
fun buildStateMachine(initialState: State, init: StateMachine.() -> Unit): StateMachine =
StateMachine(initialState).apply(init)
}
private lateinit var currentState: StateWrapper
private val stateList = mutableListOf<StateWrapper>()
fun state(state: State, init: StateWrapper.() -> Unit) {
if (stateList.firstOrNull { it.state == state } != null) {
throw IllegalStateException("Duplicate state found")
}
val stateWrapper = StateWrapper(state).apply(init)
stateList += stateWrapper
if (state == initialState) {
currentState = stateWrapper
}
}
fun eventOccurred(event: Event) {
val edge = currentState.getEdgeForEvent(event) ?: return
val newState = edge.enterEdge()
val stateWrapper = stateList.first { newState == it.state }
stateWrapper.enterState()
currentState = stateWrapper
}
}
// Example
fun example() {
val stateMachine = StateMachine.buildStateMachine(initialState) {
state(PlayerState.Uninitialized) {
edge(PlayerEvent.InitializePlaying, PlayerState.ReadyToPlay)
edge(PlayerEvent.InitializeRecording, PlayerState.ReadyToRecord)
}
state(PlayerState.ReadyToRecord) {
action {
view.prepareToRecord()
view.updateTimer(0)
}
edge(PlayerEvent.StartRecording, PlayerState.Recording) {
action { }
}
edge(PlayerEvent.StopRecording, PlayerState.ReadyToRecord)
}
state(PlayerState.ReadyToPlay) {
action { }
edge(PlayerEvent.Trash, PlayerState.ReadyToRecord) {
action { }
}
edge(PlayerEvent.StartPlaying, PlayerState.Playing) {
action { }
}
edge(PlayerEvent.Save, PlayerState.Saved) {
action { }
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment