Skip to content

Instantly share code, notes, and snippets.

@beccadax
Last active July 28, 2016 14:38
Show Gist options
  • Save beccadax/8b89db56de8bd113b6c5371484a81305 to your computer and use it in GitHub Desktop.
Save beccadax/8b89db56de8bd113b6c5371484a81305 to your computer and use it in GitHub Desktop.
HierarchicalTurnstyleState with Functioning/Broken implemented with a generic type
// Untested; consider this a sketch
typealias HierarchicalTurnstyleState = StatusState<FunctioningTurnstyleState>
enum StatusState<MachineState: StateType>: StateType {
case Functioning(MachineState)
case Broken(oldState: MachineState)
typealias Event = StatusEvent<MachineState.Event>
typealias Command = MachineState.Command
static let initialState = .Functioning(MachineState.initialState)
mutating func handleEvent(event: Event) -> Command? {
switch (self, event) {
case (.Functioning(let state), .MachineDidFail):
self = .Broken(oldState: state)
case (.Functioning(var state), .Machine(let event)):
let command = state.handleEvent(event)
self = .Functioning(state)
return command
case (.Broken(let oldState), .MachineRepairDidComplete):
self = .Functioning(oldState)
default:
break
}
return nil
}
}
enum StatusEvent<MachineEvent> {
case MachineDidFail
case MachineRepairDidComplete
case Machine(MachineEvent)
}
// If you want to maintain source compatibility when constructing events:
extension StatusEvent where MachineEvent == FunctioningTurnstyleState {
static func InsertCoin(value: Int) -> StatusEvent {
return .Machine(.InsertCoin(value: value))
}
static var AdmitPerson: StatusEvent {
return .Machine(.AdmitPerson)
}
}
enum FunctioningTurnstyleState: StateType {
case Locked(credit: Int)
case Unlocked
enum Event {
case InsertCoin(value: Int)
case AdmitPerson
}
enum Command {
case SoundAlarm
case CloseDoors
case OpenDoors
}
static let initialState = FunctioningTurnstyleState.Locked(credit: 0)
private static let farePrice = 50
mutating func handleEvent(event: Event) -> Command? {
// Now somewhat simpler because the turnstyle can't be broken.
switch (self, event) {
case (.Locked(let credit), .InsertCoin(let value)):
let newCredit = credit + value
if newCredit >= TurnstyleState.farePrice {
self = .Unlocked
return .OpenDoors
} else {
self = .Locked(credit: newCredit)
}
case (.Locked, .AdmitPerson):
return .SoundAlarm
case (.Unlocked, .AdmitPerson):
self = .Locked(credit: 0)
return .CloseDoors
default:
break // or maybe throw, etc
}
return nil
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment