Pattern Observer
Fa parte della sezione Programmazione orientata agli oggetti del percorso R di Coddy. Lezione 44 di 57.
Il pattern observer permette a un oggetto, il subject, di comunicare ad altro codice quando succede qualcosa senza sapere chi sta ascoltando. Il subject mantiene un elenco di funzioni callback, gli observer, e le chiama tutte a ogni evento:
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")Output:
logger saw click
counter saw click Un osservatore può essere una chiusura con un proprio stato. Questo conta gli eventi che ha visto, mentre il soggetto non sa nulla del conteggio:
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())Output:
[1] 3Memorizzare gli osservatori con dei nomi permette loro di annullare l’iscrizione. observers[[name]] <<- NULL ne rimuove uno, mentre gli altri continuano a ricevere gli eventi:
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")Output:
ada: first
bo: first
bo: second Il soggetto determina quali dati trasporta un evento. Un ticker dei prezzi passa il prezzo precedente e quello nuovo, e invia una notifica solo quando il prezzo cambia davvero:
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)Output:
price 10 -> 12
price 12 -> 9 Sfida
FacileCompleta la funzione factory make_newsletter() in Newsletter.R. Mantiene i callback dei lettori in una lista privata con nomi e restituisce tre funzioni:
subscribe(name, f)memorizza il callback con quel nome (una nuova sottoscrizione sostituisce quella precedente);unsubscribe(name)lo rimuove;publish(title)chiama ogni callback con il titolo, nell’ordine di sottoscrizione, e restituisce quanti lettori sono stati avvisati.
Il codice fornito legge i comandi sub ada, unsub ada e post Big news e assegna a ogni lettore un callback che stampa ada got: Big news. Per ogni post stampa il titolo, le righe dei callback e il numero di lettori avvisati.
Il tuo codice va inserito in Newsletter.R. main.R contiene il codice di input/output fornito e non può essere modificato.
Provalo tu
source("Newsletter.R")
# Codice di input/output fornito: lascialo così com'è
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 = "")
}
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Nozioni di base su S3
Lavorare con i fileListe con una classeFunzioni costruttriciMetodi di stampa e formattazioneValidatori e funzioni di supportoRipasso - Temperature4Incapsulamento in R
Chiusure come oggettiAmbienti come oggettiFunzioni di accessoProteggere lo statoRiepilogo - Parcometro2Generics e metodi S3
Generics e UseMethodMetodi predefinitiMetodi per i generics di baseNozioni di base su NextMethodRiepilogo - Aree delle forme5Classi S4
Definire le classi S4Slot e accessoControlli di validitàMetodi showRiepilogo - Catalogo prodotti8Classi di riferimento
Definizione delle classi di riferimentoMetodi e aggiornamenti dei campiSemantica della copiaEreditarietà e callSuperRiepilogo - coda di attività11Progetto: Gestione della biblioteca
Libri e iscrittiPrestito dei libriEsercitati da solo: Compilatore R online