Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Turn handler typealiases into functional interfaces #189

Closed
gabrielittner opened this issue Aug 7, 2021 · 1 comment · Fixed by #212
Closed

Turn handler typealiases into functional interfaces #189

gabrielittner opened this issue Aug 7, 2021 · 1 comment · Fixed by #212
Labels
enhancement New feature or request good first issue Good for newcomers
Milestone

Comments

@gabrielittner
Copy link
Member

When you build a state machine we ideally want something like this

class MyStateMachine(...) : FlowReduxStateMachine(...) {
  init {
    spec {
       inState<MyState> {
         on(::onAction1TransisitionToA)
         on(::onAction2DoSomethingElse)
       }
    }
  }
}

suspend fun onAction1TransisitionToA(action: Action1, stateSnapshot: MyState): ChangeState<MyState> {
  // do stuff
}

suspend fun onAction2DoSomethingElse(action: Action2, stateSnapshot: MyState): ChangeState<MyState> {
  // do stuff
}

Having the handlers as standalone methods lets you easily test them without having to build the full state machine. Method references in the DSL help a lot with keeping the definition itself readable when the state machine gets bigger.

The issue arises when the handler needs some dependency. You either need to

  1. move the function into the state machine which hurts testability
  2. add the parameter to the function, which would mean to stop using method references
  3. put the function into it's own class that injects the required parameters

The latter would look like this

class MyStateMachine(
  action1TransisitionToA: Action1TransisitionToA,
  action2DoSomethingElse: Action2DoSomethingElse,
   ...
) : FlowReduxStateMachine(...) {
  init {
    spec {
       inState<MyState> {
         on(action1TransisitionToA::handle)
         on(action2DoSomethingElse::handle)
       }
    }
  }
}

class Action1TransisitionToA(...) {
  suspend fun handle(action: Action1, stateSnapshot: MyState): ChangeState<MyState> {
    // do stuff
  }
}

class Action2DoSomethingElse(...) {
  suspend fun handle(action: Action2, stateSnapshot: MyState): ChangeState<MyState> {
    // do stuff
  }
}

This works with everything that we have however we could improve the experience by turning

typealias OnActionHandler<InputState, S, A> = suspend (action: A, state: InputState) -> ChangeState<S>

into

fun interface OnActionHandler<InputState, S, A> {
  suspend fun handle(action: A, state: InputState): ChangeState<S>
}

That way those classes can implement the interface which makes implementing it easier and also prevents any unused_parameter warning on the function. Any existing usage and usecase would still work like before because fun interface support SAM conversion.

@sockeqwe
Copy link
Collaborator

sockeqwe commented Aug 8, 2021

Good idea! That way we can have the best (compromise) of all worlds!

Btw. I will be on vacation next 2 weeks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers
Development

Successfully merging a pull request may close this issue.

2 participants