Beobachtermuster
Teil des Abschnitts Objektorientierte Programmierung der R-Journey von Coddy. Lektion 44 von 57.
Das Observer-Muster ermöglicht es einem Objekt, dem Subject, anderen Code darüber zu informieren, wenn etwas geschieht, ohne zu wissen, wer zuhört. Das Subject verwaltet eine Liste von Callback-Funktionen, den Observern, und ruft jede davon bei jedem Ereignis auf:
make_subject <- function() {
observers <- list()
list(
subscribe = function(f) observers[[length(observers) + 1]] <<- f,
notify = function(event) for (f in observers) f(event)
)
}
button <- make_subject()
button$subscribe(function(e) cat("logger saw", e, "\n"))
button$subscribe(function(e) cat("counter saw", e, "\n"))
button$notify("click")Ausgabe:
logger saw click
counter saw click Ein Beobachter kann eine Closure mit einem eigenen Zustand sein. Diese zählt die Ereignisse, die sie gesehen hat, während das Subjekt nichts vom Zählen weiß:
make_subject <- function() {
observers <- list()
list(subscribe = function(f) observers[[length(observers) + 1]] <<- f,
notify = function(event) for (f in observers) f(event))
}
make_counter <- function() {
n <- 0
list(observe = function(event) n <<- n + 1, count = function() n)
}
door <- make_subject()
c1 <- make_counter()
door$subscribe(c1$observe)
for (e in c("open", "close", "open")) door$notify(e)
print(c1$count())Ausgabe:
[1] 3Das Speichern von Beobachtern unter Namen ermöglicht es ihnen, sich abzumelden. observers[[name]] <<- NULL entfernt einen, während die anderen weiterhin Ereignisse empfangen:
make_subject <- function() {
observers <- list()
list(
subscribe = function(name, f) observers[[name]] <<- f,
unsubscribe = function(name) observers[[name]] <<- NULL,
notify = function(event) for (f in observers) f(event)
)
}
s <- make_subject()
s$subscribe("ada", function(e) cat("ada:", e, "\n"))
s$subscribe("bo", function(e) cat("bo:", e, "\n"))
s$notify("first")
s$unsubscribe("ada")
s$notify("second")Ausgabe:
ada: first
bo: first
bo: second Das Subjekt legt fest, welche Daten ein Ereignis überträgt. Ein Preis-Ticker übergibt den alten und den neuen Preis und benachrichtigt nur, wenn sich der Preis tatsächlich ändert:
make_ticker <- function(price) {
observers <- list()
list(
subscribe = function(f) observers[[length(observers) + 1]] <<- f,
set_price = function(new) {
if (new == price) return(invisible(NULL))
old <- price
price <<- new
for (f in observers) f(old, new)
}
)
}
t <- make_ticker(10)
t$subscribe(function(old, new) cat("price", old, "->", new, "\n"))
for (p in c(12, 12, 9)) t$set_price(p)Ausgabe:
price 10 -> 12
price 12 -> 9 Aufgabe
EinfachVervollständige die Fabrik make_newsletter() in Newsletter.R. Sie bewahrt die Rückruffunktionen ihrer Leser in einer privaten benannten Liste auf und gibt drei Funktionen zurück:
subscribe(name, f)speichert die Rückruffunktion unter dem Namen (ein erneutes Abonnieren ersetzt sie);unsubscribe(name)entfernt sie;publish(title)ruft jede Rückruffunktion mit dem Titel in der Reihenfolge der Abonnements auf und gibt zurück, wie viele benachrichtigt wurden.
Der bereitgestellte Code liest die Befehle sub ada, unsub ada und post Big news ein und gibt jedem Leser eine Rückruffunktion, die ada got: Big news ausgibt. Für jeden Beitrag gibt er den Titel, die Zeilen der Rückruffunktionen und die Anzahl der Benachrichtigten aus.
Dein Code gehört in Newsletter.R. main.R enthält den bereitgestellten Eingabe-/Ausgabecode und darf nicht bearbeitet werden.
Probier es selbst
source("Newsletter.R")
# Gelieferter Input/Output-Code: belasse ihn wie er ist
input <- suppressWarnings(readLines(file("stdin")))
news <- make_newsletter()
reader <- function(name) {
force(name)
function(title) cat(" ", name, " got: ", title, "\n", sep = "")
}
for (line in input) {
p <- strsplit(line, " ")[[1]]
if (p[1] == "sub") news$subscribe(p[2], reader(p[2]))
if (p[1] == "unsub") news$unsubscribe(p[2])
if (p[1] == "post") {
title <- paste(p[-1], collapse = " ")
cat("post: ", title, "\n", sep = "")
n <- news$publish(title)
cat(" notified ", n, "\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