Patron Observateur
Fait partie de la section Programmation orientée objet du Journey R de Coddy. Leçon 44 sur 57.
Le modèle observer permet à un objet, le subject, d’indiquer à un autre code que quelque chose s’est produit sans savoir qui écoute. Le sujet conserve une liste de fonctions de rappel, les observateurs, et appelle chacune d’elles à chaque événement :
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")Sortie :
logger saw click
counter saw click Un observateur peut être une fermeture avec son propre état. Celui-ci compte les événements qu’il a vus, tandis que le sujet ne sait rien du comptage :
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())Sortie :
[1] 3Le stockage des observateurs sous des noms leur permet de se désabonner. observers[[name]] <<- NULL en supprime un, et les autres continuent de recevoir les événements :
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")Sortie :
ada: first
bo: first
bo: second Le sujet détermine quelles données un événement transporte. Un ticker de prix transmet l’ancien et le nouveau prix, et ne notifie que lorsque le prix change réellement :
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)Sortie :
price 10 -> 12
price 12 -> 9 Défi
FacileComplétez la fabrique make_newsletter() dans Newsletter.R. Elle conserve les fonctions de rappel de ses lecteurs dans une liste nommée privée et renvoie trois fonctions :
subscribe(name, f)stocke la fonction de rappel sous le nom indiqué (un nouvel abonnement remplace le précédent) ;unsubscribe(name)la supprime ;publish(title)appelle chaque fonction de rappel avec le titre, dans l’ordre des abonnements, et renvoie le nombre de fonctions de rappel qui ont été appelées.
Le code fourni lit les commandes sub ada, unsub ada et post Big news, et fournit à chaque lecteur une fonction de rappel qui affiche ada got: Big news. Pour chaque publication, il affiche le titre, les lignes des fonctions de rappel et le nombre de fonctions de rappel appelées.
Votre code doit être placé dans Newsletter.R. main.R contient le code d’entrée/sortie fourni et ne peut pas être modifié.
Essayez vous-même
source("Newsletter.R")
# Code d'entrée/sortie fourni : le garder tel quel
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 = "")
}
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation orientée objet
1Bases de S3
Travailler avec les fichiersListes avec une classeFonctions constructricesMéthodes print et formatValidateurs et fonctions utilitairesRécapitulatif - Températures4Encapsulation dans R
Les fermetures comme objetsLes environnements comme objetsFonctions d’accèsProtéger l’étatRécapitulatif - Parcmètre2S3 : génériques et méthodes
Génériques et UseMethodMéthodes par défautMéthodes pour les génériques de baseNotions de base sur NextMethodRécapitulatif - Aires des formes5Classes S4
Définir des classes S4Slots et accèsVérifications de validitéMéthodes showRécapitulatif - Catalogue de produits8Classes de référence
Définition des classes de référenceMéthodes et mises à jour des champsSémantique de copieHéritage et callSuperRécapitulatif - File d’attente des tâches11Projet : gestion de bibliothèque
Livres et membresEmprunter des livres3Héritage S3
Vecteurs de classesOrdre de recherche des méthodesÉtendre avec NextMethodVérifier avec inherits()Récapitulatif - Paie du personnelEntraînez-vous par vous-même : Compilateur R en ligne