Skip to content

Instantly share code, notes, and snippets.

@kristopherjohnson
Last active March 23, 2016 10:00
Show Gist options
  • Save kristopherjohnson/4bcb89e07a5ece10e305 to your computer and use it in GitHub Desktop.
Save kristopherjohnson/4bcb89e07a5ece10e305 to your computer and use it in GitHub Desktop.
Swift implementation of Robert C. Martin's turnstile state-machine example
// Implementation of Robert C. Martin's turnstile state-machine example
/// Defines the "events" that can be invoked on a turnstile-like object.
protocol TurnstileEventHandler {
func onCoin()
func onPass()
}
/// Defines the "actions" that a turnstile can take due to an event or state transition.
protocol TurnstileActionDelegate: class {
func unlock()
func alarm()
func lock()
func thankYou()
}
/// Finite state machine that accepts `TurnstileEventHandler` events
/// and invokes actions on a `TurnstileActionDelegate`.
class TurnstileStateMachine {
/// Base class for states.
private class State {
weak var stateMachine: TurnstileStateMachine?
init(_ stateMachine: TurnstileStateMachine?) {
self.stateMachine = stateMachine
}
func changeState(newState: State) {
stateMachine?.state = newState
}
func onEntry() { print("No entry action") }
func onCoin() { print("No transition for coin") }
func onPass() { print("No transition for pass") }
func unlock() { stateMachine?.unlock() }
func alarm() { stateMachine?.alarm() }
func lock() { stateMachine?.lock() }
func thankYou() { stateMachine?.thankYou() }
}
/// Definitions of `locked` state transitions and actions.
private class Locked: State {
override func onEntry() { lock() }
override func onCoin() { changeState(Unlocked(stateMachine)) }
override func onPass() { alarm() }
}
/// Definitions of `unlocked` state transitions and actions.
private class Unlocked: State {
override func onEntry() { unlock() }
override func onCoin() { thankYou() }
override func onPass() { changeState(Locked(stateMachine)) }
}
private var state: State! {
didSet {
if state != nil { state.onEntry() }
}
}
weak var delegate: TurnstileActionDelegate?
init() {
self.state = Locked(self)
}
}
extension TurnstileStateMachine: TurnstileEventHandler {
func onCoin() { state.onCoin() }
func onPass() { state.onPass() }
}
extension TurnstileStateMachine: TurnstileActionDelegate {
func unlock() { delegate?.unlock() }
func alarm() { delegate?.alarm() }
func lock() { delegate?.lock() }
func thankYou() { delegate?.thankYou() }
}
/// A concrete implementation of a turnstile, which accepts
/// `TurnstileEventHandler` events and implements the
/// `TurnstileActionDelegate` actions.
class Turnstile {
let stateMachine = TurnstileStateMachine()
init() {
stateMachine.delegate = self
}
}
/// The public interface to `Turnstile`, which just passes the event to its state machine.
extension Turnstile: TurnstileEventHandler {
func onCoin() { stateMachine.onCoin() }
func onPass() { stateMachine.onPass() }
}
/// The actions triggered by state machine transitions.
extension Turnstile: TurnstileActionDelegate {
func unlock() { print("unlock") }
func alarm() { print("alarm") }
func lock() { print("lock") }
func thankYou() { print("thankYou") }
}
// Test
let t: TurnstileEventHandler = Turnstile()
t.onCoin() // "unlock"
t.onPass() // "lock"
t.onCoin() // "unlock"
t.onCoin() // "thankYou"
t.onPass() // "lock"
t.onPass() // "alarm"
@kristopherjohnson
Copy link
Author

Robert C Martin's "UML Tutorial: Finite State Machines": http://www.objectmentor.com/resources/articles/umlfsm.pdf

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment