Menu
CoddyTech

Mẫu State

Thuộc phần Lập trình hướng đối tượng trong hành trình R của Coddy. Bài học 48 trên 57.

Đối tượng trong mẫu trạng thái hoạt động khác nhau tùy theo trạng thái hiện tại. Mỗi trạng thái là một danh sách các hàm xử lý, và đối tượng chuyển từng yêu cầu đến các hàm xử lý của trạng thái mà nó đang ở:

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
}

Đầu ra:

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

Trình xử lý nhận một sự kiện và trả về trạng thái tiếp theo cùng một phản hồi, vì vậy các chuyển đổi là dữ liệu chứ không phải một chuỗi if. Cửa xoay mở khóa khi có người bỏ xu vào và khóa lại sau khi có người đi qua:

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")
}

Đầu ra:

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

Bản thân máy là một closure nhỏ: nó giữ trạng thái hiện tại, tra cứu trình xử lý và trả lời ignored cho sự kiện mà trạng thái đó không biết:

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")

Đầu ra:

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

Một trạng thái mới là một mục mới trong danh sách. Máy không thay đổi, và chỉ những trạng thái dẫn đến trạng thái mới mới cần một hàm xử lý mới:

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")
}

Đầu ra:

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

Thử thách

Dễ

Hoàn thành cửa quay trong Turnstile.R. Danh sách states có các trạng thái locked và unlocked, mỗi hàm xử lý trả về list(to = ..., say = ...):

  • locked: coin mở khóa và chuyển sang unlocked; push vẫn giữ trạng thái khóa với blocked;
  • unlocked: coin vẫn giữ trạng thái mở khóa với thanks, already open; push khóa cửa, trả về welcome và tính thêm một lượt qua.

Sau đó hoàn thành make_turnstile(), hàm này bắt đầu ở trạng thái khóa và trả về handle(event) (phản hồi, hoặc ignored nếu gặp sự kiện không xác định), state() và passes(). Tính thêm một lượt qua mỗi khi xử lý push trong trạng thái mở khóa. Code được cung cấp đọc các sự kiện và in từng phản hồi cùng trạng thái mới, sau đó in số lượt qua.

Code của bạn nằm trong Turnstile.R. main.R chứa code đầu vào/đầu ra được cung cấp và bạn không thể chỉnh sửa tệp này.

Tự làm thử

source("Turnstile.R")

# Đoạn code đầu vào/đầu ra đã được cung cấp: giữ nguyên
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 iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch R online