우석
연습문제 결과
sealed class ElevatorCommand {
abstract val floor: Int
data class Call(override val floor: Int) : ElevatorCommand()
data class Select(override val floor: Int) : ElevatorCommand()
data class Fix(override val floor: Int) : ElevatorCommand()
}
sealed class ElevatorState {
abstract val startFloor: Int
data class Moving(val toFloor: Int) : ElevatorState() {
override val startFloor get() = toFloor
}
data class Stopped(val floor: Int) : ElevatorState() {
override val startFloor get() = floor
}
data class Broken(val floor: Int) : ElevatorState() {
override val startFloor get() = floor
}
}
sealed class ElevatorEvent {
data class ButtonPressed(val floor: Int) : ElevatorEvent()
data class ElevatorMoved(val fromFloor: Int, val toFloor: Int) : ElevatorEvent()
data class Broken(val floor: Int) : ElevatorEvent()
data class Fixed(val floor: Int) : ElevatorEvent()
}
fun foldElevatorEvents(events: List<ElevatorEvent>): ElevatorState =
events.fold(ElevatorState.Stopped(0) as ElevatorState) { state, event ->
when (event) {
is ElevatorEvent.Broken ->
ElevatorState.Broken(state.startFloor)
is ElevatorEvent.ButtonPressed ->
if (state is ElevatorState.Broken) state
else if (event.floor != state.startFloor) ElevatorState.Moving(event.floor)
else ElevatorState.Stopped(event.floor)
is ElevatorEvent.ElevatorMoved ->
if (state is ElevatorState.Broken) state
else ElevatorState.Stopped(event.toFloor)
is ElevatorEvent.Fixed -> ElevatorState.Stopped(event.floor)
}
}
fun handleElevatorCommand(state: ElevatorState, command: ElevatorCommand): List<ElevatorEvent> =
when (command) {
is ElevatorCommand.Call, is ElevatorCommand.Select ->
if (state is ElevatorState.Broken) listOf(ElevatorEvent.Broken(state.startFloor))
else if (command.floor != state.startFloor) listOf(
ElevatorEvent.ButtonPressed(command.floor),
ElevatorEvent.ElevatorMoved(state.startFloor, command.floor)
)
else emptyList()
is ElevatorCommand.Fix ->
if (state is ElevatorState.Broken) listOf(ElevatorEvent.Fixed(state.startFloor))
else emptyList()
}
연습문제 6.1
@Test
fun `initially with no events it is stopped at floor 0`() {
val state = foldElevatorEvents(emptyList())
expectThat(state).isEqualTo(ElevatorState.Stopped(floor = 0))
}
@Test
fun `ButtonPressed for a different floor yields Moving state`() {
val events = listOf(
ElevatorEvent.ButtonPressed(floor = 3)
)
val state = foldElevatorEvents(events)
expectThat(state).isEqualTo(ElevatorState.Moving(toFloor = 3))
}
@Test
fun `ButtonPressed for the same floor yields Stopped state`() {
val events = listOf(
ElevatorEvent.ButtonPressed(floor = 0)
)
val state = foldElevatorEvents(events)
expectThat(state).isEqualTo(ElevatorState.Stopped(floor = 0))
}
@Test
fun `ElevatorMoved always yields Stopped at toFloor`() {
val events = listOf(
ElevatorEvent.ElevatorMoved(fromFloor = 5, toFloor = 2)
)
val state = foldElevatorEvents(events)
expectThat(state).isEqualTo(ElevatorState.Stopped(floor = 2))
}
@Test
fun `mixed sequence of ButtonPressed and ElevatorMoved folds correctly`() {
val events = listOf(
ElevatorEvent.ButtonPressed(floor = 2),
ElevatorEvent.ElevatorMoved(fromFloor = 0, toFloor = 2),
ElevatorEvent.ButtonPressed(floor = 5),
ElevatorEvent.ElevatorMoved(fromFloor = 2, toFloor = 5)
)
val state = foldElevatorEvents(events)
expectThat(state).isEqualTo(ElevatorState.Stopped(floor = 5))
}
연습문제 6.2
@Test
fun `elevator opens the doors at the called floor`() {
val events = listOf<ElevatorEvent>(ElevatorEvent.ElevatorMoved(fromFloor = 0, toFloor = 3))
val newEvents = processCommand(events, ElevatorCommand.Call(floor = 5))
expectThat(foldElevatorEvents(newEvents))
.isEqualTo(ElevatorState.Stopped(floor = 5))
}
@Test
fun `elevator opens the doors at the selected floor when someone presses a floor button`() {
val events = listOf<ElevatorEvent>(ElevatorEvent.ElevatorMoved(fromFloor = 0, toFloor = 5))
val newEvents = processCommand(events, ElevatorCommand.Select(floor = 10))
expectThat(foldElevatorEvents(newEvents))
.isEqualTo(ElevatorState.Stopped(floor = 10))
}
@Test
fun `elevator doesn't move if someone selects the same floor it's already on`() {
val events = listOf(
ElevatorEvent.ButtonPressed(floor = 10),
ElevatorEvent.ElevatorMoved(fromFloor = 0, toFloor = 10)
)
val newEvents = processCommand(events, ElevatorCommand.Select(floor = 10))
expectThat(foldElevatorEvents(newEvents))
.isEqualTo(ElevatorState.Stopped(floor = 10))
}
private fun processCommand(
events: List<ElevatorEvent>,
command: ElevatorCommand
): List<ElevatorEvent> =
events + handleElevatorCommand(foldElevatorEvents(events), command)
연습문제 6.3
@Test
fun `if elevator is broken it won't move`() {
val events = listOf<ElevatorEvent>(ElevatorEvent.Broken(0))
val newEvents = events + handleElevatorCommand(
foldElevatorEvents(events),
ElevatorCommand.Call(5)
)
expectThat(foldElevatorEvents(newEvents)).isEqualTo(ElevatorState.Broken(0))
}
@Test
fun `if elevator is fixed change status`() {
val events = listOf<ElevatorEvent>(ElevatorEvent.Broken(0))
val newEvents = events + handleElevatorCommand(
foldElevatorEvents(events),
ElevatorCommand.Fix(0)
)
expectThat(foldElevatorEvents(newEvents)).isEqualTo(ElevatorState.Stopped(0))
}
만혁
연습문제 6.1
// ElevatorState.kt
sealed class ElevatorState
data class WaitingAt(val floor: Int) : ElevatorState() {
fun handle(command: ElevatorCommand): ElevatorState = when (command) {
is CallElevator -> MovingTo(to = command.floor, from = floor)
is SelectFloor -> MovingTo(to = command.destination, from = floor)
}
}
data class MovingTo(val to: Int, val from: Int): ElevatorState() {
fun handle(command: ElevatorCommand) = this
}
// ElevatorCommand.kt
sealed class ElevatorCommand
data class CallElevator(val floor: Int): ElevatorCommand()
data class SelectFloor(val destination: Int) : ElevatorCommand()
// Elevator.kt
fun handle(state: ElevatorState, command: ElevatorCommand): ElevatorState {
return when (state) {
is MovingTo -> state.handle(command)
is WaitingAt -> state.handle(command)
}
}
// ElevatorTest.kt
class ElevatorTest {
@Test
fun waitToMove() {
val initState = WaitingAt(1)
val command = SelectFloor(5)
val handled = handle(initState, command)
Assertions.assertEquals(MovingTo(5, 1), handled)
}
}
연습문제 6.2
// ElevatorState.kt
sealed class ElevatorState
data class WaitingAt(val floor: Int) : ElevatorState() {
fun commandToEvents(command: ElevatorCommand): List<ElevatorEvent> = when (command) {
is CallElevator -> listOf(
ElevatorCalled(floor = command.floor),
ElevatorMoved(from = floor, to = command.floor)
)
is SelectFloor -> listOf(
FloorSelected(command.destination),
ElevatorMoved(to = command.destination, from = floor)
)
}
}
data class MovingTo(val to: Int, val from: Int): ElevatorState() {
fun commandToEvents(command: ElevatorCommand) = this
}
// ElevatorCommand.kt
sealed class ElevatorCommand
data class CallElevator(val floor: Int): ElevatorCommand()
data class SelectFloor(val destination: Int) : ElevatorCommand()
// ElevatorEvent.kt
sealed class ElevatorEvent
data class ElevatorMoved(val from: Int, val to: Int): ElevatorEvent()
data class ElevatorArrived(val floor: Int): ElevatorEvent()
data class FloorSelected(val floor: Int): ElevatorEvent()
data class ElevatorCalled(val floor: Int): ElevatorEvent()
// Elevator.kt
fun handleCommand(state: ElevatorState, command: ElevatorCommand) : List<ElevatorEvent> {
return when(state) {
is MovingTo -> emptyList()
is WaitingAt -> {
when(command) {
is CallElevator -> state.commandToEvents(command)
is SelectFloor -> state.commandToEvents(command)
}
}
}
}
fun evolve(state: ElevatorState, event: ElevatorEvent): ElevatorState = when (event) {
is ElevatorMoved -> MovingTo(to = event.to, from = event.from)
is ElevatorArrived -> WaitingAt(event.floor)
is FloorSelected -> state // 상태 변화 없음
is ElevatorCalled -> state // 상태 변화 없음
}
fun fold(initial: ElevatorState, events: List<ElevatorEvent>): ElevatorState =
events.fold(initial) { state, event -> evolve(state, event) }
// ElevatorTest.kt
class ElevatorTest {
@Test
fun addEvents() {
val initState = WaitingAt(floor = 1)
val events = listOf(
ElevatorCalled(3),
ElevatorMoved(from = 1, to = 3),
ElevatorArrived(3),
FloorSelected(5),
ElevatorMoved(from = 3, to = 5),
ElevatorArrived(floor = 5)
)
val result = fold(initState, events)
Assertions.assertEquals(
result, WaitingAt(floor = 5)
)
}
}
연습문제 6.3
// ElevatorState.kt
sealed class ElevatorState
data class WaitingAt(val floor: Int) : ElevatorState() {
fun handle(command: ElevatorCommand): ElevatorState = when (command) {
is CallElevator -> MovingTo(to = command.floor, from = floor)
is SelectFloor -> MovingTo(to = command.destination, from = floor)
is BreakDown -> MovingTo(to = 1, from = floor)
is RepairElevator -> WaitingAt(floor = 1)
}
fun decide(command: ElevatorCommand): List<ElevatorEvent> = when (command) {
is CallElevator -> listOf(
ElevatorCalled(floor = command.floor),
ElevatorMoved(from = floor, to = command.floor)
)
is SelectFloor -> listOf(
FloorSelected(command.destination),
ElevatorMoved(to = command.destination, from = floor)
)
is RepairElevator -> listOf(
ElevatorRepaired(),
)
is BreakDown -> listOf(
ElevatorMoved(from = floor, to = 1)
)
}
}
data class MovingTo(val to: Int, val from: Int) : ElevatorState() {
fun handle(command: ElevatorCommand) = this
fun decide(command: ElevatorCommand): List<ElevatorEvent> = emptyList()
}
data class Broken(val cause: String) : ElevatorState() {
fun handle(command: ElevatorCommand): ElevatorState = this
fun decide(command: ElevatorCommand): List<ElevatorEvent> = when (command) {
is CallElevator, is SelectFloor, is BreakDown -> emptyList()
is RepairElevator -> listOf(ElevatorRepaired())
}
}
// ElevatorCommand.kt
data class CallElevator(val floor: Int): ElevatorCommand()
data class SelectFloor(val destination: Int) : ElevatorCommand()
data class BreakDown(val cause: String): ElevatorCommand()
class RepairElevator : ElevatorCommand()
// ElevatorEvent.kt
sealed class ElevatorEvent
data class ElevatorMoved(val from: Int, val to: Int): ElevatorEvent()
data class ElevatorArrived(val floor: Int): ElevatorEvent()
data class FloorSelected(val floor: Int): ElevatorEvent()
data class ElevatorCalled(val floor: Int): ElevatorEvent()
data class ElevatorBroken(val cause: String): ElevatorEvent()
class ElevatorRepaired : ElevatorEvent()
// Elevator.kt
fun handleCommand(state: ElevatorState, command: ElevatorCommand): List<ElevatorEvent> = when (state) {
is MovingTo -> state.decide(command)
is WaitingAt -> state.decide(command)
is Broken -> state.decide(command)
}
fun evolve(state: ElevatorState, event: ElevatorEvent): ElevatorState = when (event) {
is ElevatorMoved -> MovingTo(to = event.to, from = event.from)
is ElevatorArrived -> WaitingAt(event.floor)
is FloorSelected -> state // 상태 변화 없음
is ElevatorCalled -> state // 상태 변화 없음
is ElevatorBroken -> Broken(cause = event.cause)
is ElevatorRepaired -> WaitingAt(floor = 1)
}
fun fold(initial: ElevatorState, events: List<ElevatorEvent>): ElevatorState =
events.fold(initial) { state, event -> evolve(state, event) }
// ElevatorTest.kt
class ElevatorTest {
@Test
fun brokenTest() {
val initState = WaitingAt(floor = 1)
val events = listOf(
ElevatorCalled(3),
ElevatorMoved(from = 1, to = 3),
ElevatorBroken("케이블 끊어짐"),
ElevatorRepaired(),
)
val result = fold(initState, events)
Assertions.assertEquals(
result, WaitingAt(floor = 1)
)
}
}