Created
August 22, 2025 17:36
-
-
Save BugInMyHEAD/a85aa7599eef1de9bae89ff5ba5fe7ef to your computer and use it in GitHub Desktop.
GenericTransState
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.buginmyhead.chatter.fundamental.architecture | |
| /** | |
| * Trans-state is a coinage to indicate transition and state together in the state machine concept. | |
| * | |
| * Example for [GenericTransState], [GenericTransition], [GenericTransition.Disposal]: | |
| * ```kotlin | |
| * sealed interface Event | |
| * sealed interface TransState : GenericTransState<TransState, State, Event> | |
| * sealed interface State : TransState | |
| * | |
| * data class Transition( | |
| * override val state: State, | |
| * val sideEffect1: Any, | |
| * val sideEffect2: Any, | |
| * ) : TransState, GenericTransition<TransState, State, Event> { | |
| * | |
| * data object Disposal : Event, GenericTransition.Disposal | |
| * | |
| * } | |
| * ``` | |
| * | |
| * @param TS trans-state: the runtime type is either [GenericTransState] or [GenericTransition]. | |
| * @param S state | |
| * @param E event | |
| */ | |
| @Suppress("UNCHECKED_CAST") | |
| interface GenericTransState<TS : GenericTransState<TS, S, E>, S : TS, E> { | |
| fun transitByEvent(event: E): TS | |
| companion object { | |
| /** | |
| * @return | |
| * [GenericTransition.state] if the receiver is [GenericTransition], | |
| * or the receiver itself otherwise. | |
| */ | |
| @JvmStatic | |
| val <TS : GenericTransState<TS, S, E>, S : TS, E> TS.state: S get() = | |
| (if (this is GenericTransition<*, *, *>) state else this) as S | |
| } | |
| } | |
| /** | |
| * Transition in the state machine concept. | |
| * | |
| * Read the documentation of [GenericTransState] for an example. | |
| * | |
| * @param S state | |
| * @param E event | |
| */ | |
| interface GenericTransition<TS : GenericTransState<TS, S, E>, S : TS, E> : GenericTransState<TS, S, E> { | |
| val state: S | |
| /** | |
| * @return [state] for [Disposal] event, or `[transitByEvent]` of [state] for other events. | |
| */ | |
| override fun transitByEvent(event: E): TS = | |
| if (event is Disposal) state else state.transitByEvent(event) | |
| /** | |
| * Implement with the custom event interface. | |
| * The meaning of this event is to reset side effect state to default | |
| * by calling [transitByEvent]. | |
| * | |
| * Read the documentation of [GenericTransState] for an example. | |
| */ | |
| interface Disposal | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.buginmyhead.chatter.fundamental.architecture | |
| import com.buginmyhead.chatter.fundamental.architecture.GenericTransState.Companion.state | |
| import io.kotest.core.spec.style.FreeSpec | |
| import io.kotest.matchers.shouldBe | |
| internal class GenericTransStateTest : FreeSpec({ | |
| "state of State should be itself" { | |
| val state = StateA | |
| state.state shouldBe state | |
| } | |
| "state of Transition should be state inside Transition" { | |
| val state = StateA | |
| val transition = Transition( | |
| state = state, | |
| ) | |
| transition.state shouldBe state | |
| } | |
| "transitByEvent of Transition with ordinary event works on state inside Transition" { | |
| val state = StateA | |
| val transition = Transition( | |
| state = state, | |
| ) | |
| transition.transitByEvent(EventA) shouldBe StateA.transitByEvent(EventA) | |
| } | |
| "transitByEvent of Transition with Disposal event returns state inside Transition" { | |
| val state = StateA | |
| val transition = Transition( | |
| state = state, | |
| ) | |
| transition.transitByEvent(Transition.Disposal) shouldBe state | |
| } | |
| }) | |
| private sealed interface Event | |
| private sealed interface TransState : GenericTransState<TransState, State, Event> | |
| private sealed interface State : TransState | |
| private data object EventA : Event | |
| private data object StateA : State { | |
| override fun transitByEvent(event: Event): TransState = Transition( | |
| state = StateB | |
| ) | |
| } | |
| private data object StateB : State { | |
| override fun transitByEvent(event: Event): TransState = Transition( | |
| state = StateA | |
| ) | |
| } | |
| private data class Transition( | |
| override val state: State | |
| ) : TransState, GenericTransition<TransState, State, Event> { | |
| data object Disposal : Event, GenericTransition.Disposal | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment