Wzorzec obserwatora
Część sekcji Programowanie obiektowe ścieżki R w Coddy. Lekcja 44 z 57.
Wzorzec obserwator pozwala obiektowi, podmiotowi, powiadamiać inny kod o wystąpieniu zdarzenia, nie wiedząc, kto nasłuchuje. Podmiot przechowuje listę funkcji zwrotnych, obserwatorów, i wywołuje każdą z nich przy każdym zdarzeniu:
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")Wynik:
logger saw click
counter saw click Obserwator może być domknięciem z własnym stanem. Ten zlicza zdarzenia, które widział, podczas gdy podmiot nie wie nic o zliczaniu:
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())Wynik:
[1] 3Przechowywanie obserwatorów pod nazwami pozwala im zrezygnować z subskrypcji. observers[[name]] <<- NULL usuwa jednego, a pozostali nadal otrzymują zdarzenia:
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")Wynik:
ada: first
bo: first
bo: second Temat decyduje o tym, jakie dane niesie zdarzenie. Ticker cenowy przekazuje starą i nową cenę oraz powiadamia tylko wtedy, gdy cena rzeczywiście się zmienia:
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)Wynik:
price 10 -> 12
price 12 -> 9 Wyzwanie
ŁatwyUzupełnij fabrykę make_newsletter() w pliku Newsletter.R. Przechowuje ona funkcje zwrotne swoich czytelników na prywatnej, nazwanej liście i zwraca trzy funkcje:
subscribe(name, f)zapisuje funkcję zwrotną pod daną nazwą (ponowna subskrypcja zastępuje poprzednią);unsubscribe(name)usuwa ją;publish(title)wywołuje każdą funkcję zwrotną z tytułem, w kolejności subskrypcji, i zwraca liczbę powiadomionych osób.
Dostarczony kod odczytuje polecenia sub ada, unsub ada i post Big news, a każdemu czytelnikowi przypisuje funkcję zwrotną, która wypisuje ada got: Big news. Dla każdego wpisu wypisuje tytuł, wiersze funkcji zwrotnych i liczbę powiadomionych osób.
Twój kod umieść w pliku Newsletter.R. Plik main.R zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.
Spróbuj swoich sił
source("Newsletter.R")
# Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
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 = "")
}
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy S3
Praca z plikamiListy z klasąFunkcje konstruktoraMetody print i formatWalidatory i funkcje pomocniczePowtórzenie — temperatury4Enkapsulacja w R
Domknięcia jako obiektyŚrodowiska jako obiektyFunkcje dostępoweOchrona stanuPodsumowanie – parkometr2Generics i metody S3
Generics i UseMethodMetody domyślneMetody dla podstawowych genericsPodstawy NextMethodPowtórzenie — pola figur5Klasy S4
Definiowanie klas S4Sloty i dostępSprawdzanie poprawnościMetody wyświetlaniaPodsumowanie – katalog produktów8Klasy referencyjne
Definiowanie klas referencyjnychMetody i aktualizacja pólSemantyka kopiowaniaDziedziczenie i callSuperPodsumowanie – kolejka zadań11Projekt: Zarządzanie biblioteką
Książki i czytelnicyWypożyczanie książek3Dziedziczenie S3
Wektory klasKolejność wyszukiwania metodRozszerzanie za pomocą NextMethodSprawdzanie za pomocą inherits()Powtórzenie — lista płac pracownikówPoćwicz samodzielnie: Kompilator R online