Patrón Observer
Parte de la sección Programación orientada a objetos del Journey de R de Coddy. Lección 44 de 57.
El patrón observer permite que un objeto, el subject, informe a otro código cuando ocurre algo sin saber quién está escuchando. El subject mantiene una lista de funciones de callback, los observers, y llama a cada una de ellas en cada 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")Salida:
logger saw click
counter saw click Un observador puede ser un cierre con su propio estado. Este cuenta los eventos que ha visto, mientras que el sujeto no sabe nada sobre el conteo:
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())Salida:
[1] 3Almacenar observadores bajo nombres les permite darse de baja. observers[[name]] <<- NULL elimina uno, y los demás siguen recibiendo eventos:
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")Salida:
ada: first
bo: first
bo: second El sujeto decide qué datos transporta un evento. Un indicador de precios pasa el precio antiguo y el nuevo, y notifica solo cuando el precio realmente cambia:
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)Salida:
price 10 -> 12
price 12 -> 9 Desafío
FácilCompleta la fábrica make_newsletter() en Newsletter.R. Mantiene las devoluciones de llamada de sus lectores en una lista privada con nombre y devuelve tres funciones:
subscribe(name, f)almacena la devolución de llamada con ese nombre (si se suscribe de nuevo, la reemplaza);unsubscribe(name)la elimina;publish(title)llama a cada devolución de llamada con el título, en el orden de suscripción, y devuelve cuántas fueron notificadas.
El código proporcionado lee los comandos sub ada, unsub ada y post Big news, y proporciona a cada lector una devolución de llamada que imprime ada got: Big news. Para cada publicación, imprime el título, las líneas de las devoluciones de llamada y el número de notificados.
Tu código va en Newsletter.R. main.R contiene el código de entrada/salida proporcionado y no se puede editar.
Pruébalo tú mismo
source("Newsletter.R")
# Código de entrada/salida suministrado: déjalo tal como está
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 = "")
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación orientada a objetos
1Fundamentos de S3
Trabajar con archivosListas con una claseFunciones constructorasMétodos de impresión y formatoValidadores y auxiliaresRepaso: temperaturas4Encapsulación en R
Cierres como objetosEntornos como objetosFunciones de accesoProtección del estadoRepaso: parquímetro2S3 Genéricos y métodos
Genéricos y UseMethodMétodos predeterminadosMétodos para genéricos baseFundamentos de NextMethodRepaso: áreas de figuras5Clases S4
Definición de clases S4Slots y accesoComprobaciones de validezMétodos showRepaso - Catálogo de productos8Clases de referencia
Definición de clases de referenciaMétodos y actualizaciones de camposSemántica de copiaHerencia y callSuperRepaso: cola de tareas11Proyecto: Gestión de biblioteca
Libros y miembrosPréstamo de librosPractica por tu cuenta: Compilador de R online