Menu
Coddy logo textTech

상태 패턴

Coddy R 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 57개 중 48번째.

상태 패턴의 객체는 현재 상태에 따라 다르게 동작합니다. 각 상태는 핸들러 함수의 목록이며, 객체는 모든 요청을 자신이 속한 상태의 핸들러에 전달합니다:

states <- list(
  green = list(label = "go", next_state = "yellow"),
  yellow = list(label = "slow down", next_state = "red"),
  red = list(label = "stop", next_state = "green")
)
current <- "green"
for (i in 1:4) {
  cat(current, ":", states[[current]]$label, "\n")
  current <- states[[current]]$next_state
}

출력:

green : go 
yellow : slow down 
red : stop 
green : go 

핸들러는 이벤트를 받아 다음 상태와 응답을 반환하므로 전환은 if의 연쇄가 아니라 데이터입니다. 개찰구는 동전이 투입되면 잠금이 해제되고 누군가 통과한 후 다시 잠깁니다:

states <- list(
  locked = list(
    coin = function() list(to = "unlocked", say = "unlocked"),
    push = function() list(to = "locked", say = "blocked")
  ),
  unlocked = list(
    coin = function() list(to = "unlocked", say = "already open"),
    push = function() list(to = "locked", say = "welcome")
  )
)
current <- "locked"
for (event in c("push", "coin", "coin", "push")) {
  r <- states[[current]][[event]]()
  current <- r$to
  cat(event, "->", r$say, "\n")
}

출력:

push -> blocked 
coin -> unlocked 
coin -> already open 
push -> welcome 

기계 자체는 작은 클로저입니다. 현재 상태를 보유하고, 핸들러를 조회하며, 상태가 알지 못하는 이벤트에 대해 ignored를 반환합니다:

make_machine <- function(states, start) {
  current <- start
  list(
    handle = function(event) {
      h <- states[[current]][[event]]
      if (is.null(h)) return("ignored")
      r <- h()
      current <<- r$to
      r$say
    },
    state = function() current
  )
}
states <- list(off = list(press = function() list(to = "on", say = "light on")),
               on = list(press = function() list(to = "off", say = "light off")))
m <- make_machine(states, "off")
for (e in c("press", "kick", "press")) cat(e, ":", m$handle(e), "|", m$state(), "\n")

출력:

press : light on | on 
kick : ignored | on 
press : light off | off 

새로운 상태는 목록에 새 항목을 추가하는 것입니다. 머신은 변경되지 않으며, 새로운 상태로 이어지는 상태에만 새로운 핸들러가 필요합니다:

states <- list(
  off = list(press = function() list(to = "on", say = "on")),
  on = list(press = function() list(to = "off", say = "off"),
            overheat = function() list(to = "broken", say = "fuse blown")),
  broken = list(repair = function() list(to = "off", say = "repaired"))
)
current <- "off"
for (e in c("press", "overheat", "press", "repair")) {
  h <- states[[current]][[e]]
  if (is.null(h)) { cat(e, ": ignored\n"); next }
  r <- h()
  current <- r$to
  cat(e, ":", r$say, "\n")
}

출력:

press : on 
overheat : fuse blown 
press : ignored
repair : repaired 
challenge icon

챌린지

쉬움

Turnstile.R에서 개찰구를 완성하세요. states 목록에는 locked와 unlocked 상태가 있으며, 각 핸들러는 list(to = ..., say = ...)를 반환합니다:

  • locked: coin은 unlocked로 잠금 해제되고, push는 blocked와 함께 locked 상태를 유지합니다;
  • unlocked: coin은 thanks, already open과 함께 unlocked 상태를 유지하고, push는 welcome과 함께 잠그며 통과 횟수를 1 증가시킵니다.

그런 다음 잠긴 상태로 시작하여 handle(event)(응답 또는 알 수 없는 이벤트일 때 ignored), state(), passes()를 반환하는 make_turnstile()을 완성하세요. unlocked 상태에서 push가 처리될 때마다 통과 횟수를 세세요. 제공된 코드는 이벤트를 읽고 각 응답을 새로운 상태와 함께 출력한 다음, 통과 횟수를 출력합니다.

코드는 Turnstile.R에 작성하세요. main.R에는 제공된 입력/출력 코드가 들어 있으며 수정할 수 없습니다.

직접 해보기

source("Turnstile.R")

# 제공된 입력/출력 코드: 그대로 유지하세요
input <- suppressWarnings(readLines(file("stdin")))
t <- make_turnstile()
for (event in input) cat(event, ": ", t$handle(event), " [", t$state(), "]\n", sep = "")
cat("passes: ", t$passes(), "\n", sep = "")
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 R 컴파일러