Zustandsmuster
Teil des Abschnitts Objektorientierte Programmierung der R-Journey von Coddy. Lektion 48 von 57.
Ein Objekt im State-Muster verhält sich je nach seinem aktuellen Zustand unterschiedlich. Jeder Zustand ist eine Liste von Handler-Funktionen, und das Objekt übergibt jede Anfrage an die Handler des Zustands, in dem es sich befindet:
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
}Ausgabe:
green : go
yellow : slow down
red : stop
green : go Handler nehmen ein Ereignis entgegen und geben den nächsten Zustand sowie eine Antwort zurück, sodass die Übergänge Daten statt einer Kette von ifs sind. Ein Drehkreuz wird entriegelt, wenn eine Münze eingeworfen wird, und wieder verriegelt, nachdem jemand hindurchgegangen ist:
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")
}Ausgabe:
push -> blocked
coin -> unlocked
coin -> already open
push -> welcome Die Maschine selbst ist eine kleine Closure: Sie speichert den aktuellen Zustand, sucht den Handler nach und liefert für ein Ereignis, das der Zustand nicht kennt, ignored zurück:
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")Ausgabe:
press : light on | on
kick : ignored | on
press : light off | off Ein neuer Zustand ist ein neuer Eintrag in der Liste. Die Maschine ändert sich nicht, und nur die Zustände, die zum neuen Zustand führen, benötigen einen neuen Handler:
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")
}Ausgabe:
press : on
overheat : fuse blown
press : ignored
repair : repaired Aufgabe
EinfachVervollständige das Drehkreuz in Turnstile.R. Die Liste states enthält die Zustände locked und unlocked, und jeder Handler gibt list(to = ..., say = ...) zurück:
- locked:
coinentsperrt mitunlocked;pushbleibt mitblockedgesperrt; - unlocked:
coinbleibt mitthanks, already openentsperrt;pushsperrt mitwelcomeund zählt einen Durchgang.
Vervollständige anschließend make_turnstile(), das gesperrt startet und handle(event) (die Antwort oder ignored für ein unbekanntes Ereignis), state() und passes() zurückgibt. Zähle einen Durchgang, sobald push im entsperrten Zustand verarbeitet wird. Der bereitgestellte Code liest Ereignisse ein und gibt jede Antwort mit dem neuen Zustand aus, anschließend die Anzahl der Durchgänge.
Dein Code gehört in Turnstile.R. main.R enthält den bereitgestellten Eingabe-/Ausgabecode und kann nicht bearbeitet werden.
Probier es selbst
source("Turnstile.R")
# Gelieferter Ein-/Ausgabe-Code: belasse ihn wie er ist
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 = "")
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1S3-Grundlagen
Arbeiten mit DateienListen mit einer KlasseKonstruktorfunktionenPrint- und Format-MethodenValidatoren und HilfsfunktionenRückblick – Temperaturen4Kapselung in R
Closures als ObjekteUmgebungen als ObjekteZugriffsfunktionenZustand schützenRückblick – Parkuhr2S3-Generics und Methoden
Generics und UseMethodStandardmethodenMethoden für Basis-GenericsGrundlagen von NextMethodWiederholung – Flächeninhalte von Formen5S4-Klassen
S4-Klassen definierenSlots und ZugriffGültigkeitsprüfungenShow-MethodenRückblick – Produktkatalog8Referenzklassen
Referenzklassen definierenMethoden und FeldänderungenKopiersemantikVererbung und callSuperZusammenfassung – Aufgabenwarteschlange11Projekt: Bibliotheksverwaltung
Bücher und MitgliederBücher ausleihenÜbe selbstständig: Online-R-Compiler