Padrão Observer
Parte da seção Programação Orientada a Objetos do Journey de R da Coddy. Lição 44 de 57.
O padrão observer permite que um objeto, o subject, informe a outro código quando algo acontece sem saber quem está ouvindo. O subject mantém uma lista de funções de callback, os observers, e chama cada uma delas a 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")Saída:
logger saw click
counter saw click Um observador pode ser uma closure com seu próprio estado. Este conta os eventos que já recebeu, enquanto o sujeito não sabe nada sobre contagem:
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())Saída:
[1] 3Armazenar observadores sob nomes permite que eles cancelem a inscrição. observers[[name]] <<- NULL remove um, e os outros continuam recebendo 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")Saída:
ada: first
bo: first
bo: second O assunto determina quais dados um evento carrega. Um ticker de preços passa o preço antigo e o novo, e notifica somente quando o preço realmente muda:
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)Saída:
price 10 -> 12
price 12 -> 9 Desafio
FácilComplete a fábrica make_newsletter() em Newsletter.R. Ela mantém os callbacks dos leitores em uma lista nomeada privada e retorna três funções:
subscribe(name, f)armazena o callback sob o nome (inscrever-se novamente o substitui);unsubscribe(name)o remove;publish(title)chama cada callback com o título, na ordem de inscrição, e retorna quantos foram notificados.
O código fornecido lê os comandos sub ada, unsub ada e post Big news, e fornece a cada leitor um callback que imprime ada got: Big news. Para cada publicação, ele imprime o título, as linhas dos callbacks e o número de notificações.
Seu código deve ficar em Newsletter.R. main.R contém o código fornecido de entrada/saída e não pode ser editado.
Experimente você mesmo
source("Newsletter.R")
# Código de entrada/saída fornecido: mantenha-o 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 lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos do S3
Trabalhando com ArquivosListas com uma ClasseFunções ConstrutorasMétodos de Impressão e FormataçãoValidadores e AuxiliaresRevisão - Temperaturas4Encapsulamento em R
Closures como ObjetosAmbientes como ObjetosFunções de AcessoProtegendo o EstadoRecapitulação - Parquímetro2Genéricos e Métodos S3
Genéricos e UseMethodMétodos padrãoMétodos para genéricos baseFundamentos de NextMethodRevisão - Áreas de formas5Classes S4
Definindo classes S4Slots e acessoVerificações de validadeMétodos showRevisão - Catálogo de produtos8Classes de Referência
Definindo Classes de ReferênciaMétodos e Atualizações de CamposSemântica de CópiaHerança e callSuperRecapitulação — Fila de Tarefas11Projeto: Gerenciamento de Biblioteca
Livros e MembrosEmpréstimo de LivrosPratique por conta própria: Compilador de R online