Last active
September 28, 2022 12:06
-
-
Save macieknajbar/93c8e2c7e9ec4324f92d3eab45452427 to your computer and use it in GitHub Desktop.
Finite State Machine
This file contains 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
class OrderPresenter( | |
private val view: OrderContract.View, | |
private val createNewOrder: CreateNewOrder, | |
private val addItemToOrder: AddItemToOrder, | |
private val permissionRequester: PermissionRequester, | |
private val scheduler: Scheduler, | |
) : OrderContract.Presenter { | |
private var state = State( | |
order = null, | |
) | |
private var fsm: OrderFSM = OrderFSM.NewOrder(context = this) | |
fun changeState(newState: OrderFSM) { | |
fsm = newState | |
} | |
private var menuItemClickVisitor: MenuItemClickVisitor = | |
MenuItemClickVisitor.Unauthenticated(context = this) | |
fun changeMenuItemClickVisitor(visitor: MenuItemClickVisitor.Authenticated) { | |
menuItemClickVisitor = visitor | |
} | |
override fun onItemClicked(itemId: String) { | |
fsm.onMenuItemClicked( | |
visitor = menuItemClickVisitor, | |
itemId = itemId, | |
) | |
} | |
fun requestPermission(permission: String, callback: () -> Unit) { | |
try { | |
permissionRequester.requestPermission( | |
permission = permission, | |
callback = { callback() } | |
) | |
} catch (e: PermissionRequester.Issue.Unauthorized) { | |
view.showError() | |
view.hideLoading() | |
} | |
} | |
fun createNewOrder() { | |
val newOrder = createNewOrder.exec() | |
state = state.copy(order = newOrder) | |
} | |
fun getOrder(): Order { | |
return checkNotNull(state.order) | |
} | |
fun addItemToOrder(itemId: String, order: Order) { | |
addItemToOrder.exec(itemId, order.id) | |
view.render() | |
view.hideLoading() | |
changeState(OrderFSM.ExistingOrder(context = this)) | |
} | |
fun showLoading() { | |
view.showLoading() | |
} | |
fun scheduleAction(after: Long, action: () -> Unit) { | |
scheduler.schedule(after, action) | |
} | |
fun onWaiting() { | |
fsm.onWaiting() | |
} | |
data class State( | |
val order: Order?, | |
) | |
} | |
abstract class MenuItemClickVisitor(protected val context: OrderPresenter) { | |
abstract fun visit(state: OrderFSM.ExistingOrder, itemId: String) | |
abstract fun visit(state: OrderFSM.NewOrder, itemId: String) | |
class Unauthenticated(context: OrderPresenter) : MenuItemClickVisitor(context) { | |
override fun visit(state: OrderFSM.ExistingOrder, itemId: String) { | |
context.requestPermission( | |
permission = PermissionRequester.ADD_ITEM_TO_ORDER, | |
callback = { | |
context.changeMenuItemClickVisitor( | |
visitor = Authenticated(context = context) | |
) | |
state.performMenuItemClicked(itemId) | |
}, | |
) | |
} | |
override fun visit(state: OrderFSM.NewOrder, itemId: String) { | |
context.requestPermission( | |
permission = PermissionRequester.ADD_ITEM_TO_ORDER, | |
callback = { | |
context.changeMenuItemClickVisitor( | |
visitor = Authenticated(context = context) | |
) | |
state.performMenuItemClicked(itemId) | |
}, | |
) | |
} | |
} | |
class Authenticated(context: OrderPresenter) : MenuItemClickVisitor(context) { | |
override fun visit(state: OrderFSM.ExistingOrder, itemId: String) { | |
state.performMenuItemClicked(itemId) | |
} | |
override fun visit(state: OrderFSM.NewOrder, itemId: String) { | |
state.performMenuItemClicked(itemId) | |
} | |
} | |
} | |
abstract class OrderFSM(protected val context: OrderPresenter) { | |
open fun onMenuItemClicked( | |
visitor: MenuItemClickVisitor, | |
itemId: String, | |
) = Unit | |
open fun onWaiting() = Unit | |
class ExistingOrder(context: OrderPresenter) : OrderFSM(context) { | |
override fun onMenuItemClicked( | |
visitor: MenuItemClickVisitor, | |
itemId: String, | |
) { | |
context.changeState(Locked(context)) | |
visitor.visit( | |
state = this, | |
itemId = itemId, | |
) | |
} | |
fun performMenuItemClicked(itemId: String) { | |
val order = context.getOrder() | |
context.addItemToOrder(itemId, order) | |
} | |
} | |
class NewOrder(context: OrderPresenter) : OrderFSM(context) { | |
override fun onMenuItemClicked( | |
visitor: MenuItemClickVisitor, | |
itemId: String, | |
) { | |
context.changeState(Locked(context)) | |
visitor.visit( | |
state = this, | |
itemId = itemId, | |
) | |
} | |
fun performMenuItemClicked(itemId: String) { | |
context.createNewOrder() | |
val order = context.getOrder() | |
context.addItemToOrder(itemId, order) | |
} | |
} | |
class Locked(context: OrderPresenter) : OrderFSM(context) { | |
init { | |
context.scheduleAction( | |
after = 200L, | |
action = { context.onWaiting() } | |
) | |
} | |
override fun onWaiting() { | |
context.showLoading() | |
} | |
} | |
} | |
interface OrderContract { | |
interface View { | |
fun render() | |
fun showLoading() | |
fun hideLoading() | |
fun showError() | |
} | |
interface Presenter { | |
fun onItemClicked(itemId: String) | |
} | |
} | |
class Order( | |
val id: String, | |
) | |
interface CreateNewOrder { | |
fun exec(): Order | |
} | |
interface AddItemToOrder { | |
fun exec(itemId: String, orderId: String) | |
} | |
interface PermissionRequester { | |
fun requestPermission(permission: String, callback: () -> Unit) | |
sealed class Issue : RuntimeException() { | |
object Unauthorized : Issue() | |
} | |
companion object { | |
const val ADD_ITEM_TO_ORDER = "AITO" | |
} | |
} | |
interface Scheduler { | |
fun schedule(after: Long, action: () -> Unit) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment