Menu
Coddy logo textTech

Patron Singleton

Fait partie de la section Programmation orientée objet du Journey R de Coddy. Leçon 42 sur 57.

Un patron de conception est une solution éprouvée à un problème de conception qui se présente encore et encore. Le patron singleton garantit qu’il existe exactement une instance de quelque chose, accessible depuis n’importe où. Dans R, une fonction d’accès crée l’instance lors de son premier appel, la conserve dans une variable créée avec local() et renvoie toujours la même :

get_config <- local({
  instance <- NULL
  function() {
    if (is.null(instance)) {
      instance <<- new.env()
      instance$theme <- "light"
    }
    instance
  }
})
a <- get_config()
a$theme <- "dark"
b <- get_config()
cat(b$theme, identical(a, b))

Sortie :

dark TRUE

L’instance est un environnement : une modification effectuée lors d’un appel est visible par toutes les autres parties du programme. Deux fonctions situées à des endroits différents écrivent dans le même journal :

get_log <- local({
  instance <- NULL
  function() {
    if (is.null(instance)) {
      instance <<- new.env()
      instance$lines <- character(0)
    }
    instance
  }
})
page_a <- function() {
  log <- get_log()
  log$lines <- c(log$lines, "a visited")
}
page_b <- function() {
  log <- get_log()
  log$lines <- c(log$lines, "b visited")
}
page_a()
page_b()
page_a()
print(get_log()$lines)

Sortie :

[1] "a visited" "b visited" "a visited"

L’instance est créée uniquement lorsqu’elle est nécessaire pour la première fois. Le comptage des créations montre qu’elle n’a lieu qu’une seule fois, quel que soit le nombre d’appels de l’accesseur :

created <- 0
get_db <- local({
  instance <- NULL
  function() {
    if (is.null(instance)) {
      created <<- created + 1
      instance <<- new.env()
    }
    instance
  }
})
for (i in 1:5) db <- get_db()
cat("created", created, "time(s)")

Sortie :

created 1 time(s)

Utilisez les singletons avec parcimonie. Un singleton est un état global : n’importe quel code peut le modifier, et un test ne peut pas démarrer avec une copie vierge sans le réinitialiser. Il convient aux éléments qui sont véritablement uniques par programme, comme un enregistreur ou les paramètres : transmettre un objet comme argument est souvent plus clair :

# plus clair pour tester : le log est passé en paramètre
process <- function(order, log) {
  log$lines <- c(log$lines, paste("processing", order$id))
}
challenge icon

Défi

Facile

Complétez le logger singleton dans Logger.R. get_logger() renvoie un environnement, créé lors du premier appel, avec le champ entries (de type caractère, vide au départ). Écrivez ensuite :

  • log_info(msg) et log_error(msg), qui ajoutent [INFO] msg ou [ERROR] msg aux entrées du logger partagé ;
  • error_count(), qui compte les entrées commençant par [ERROR].

Le code fourni lit les lignes info,started ou error,disk full, journalise chacune d’elles, puis affiche chaque entrée, le nombre d’erreurs et indique si deux appels de get_logger() renvoient le même objet.

Votre code doit être placé dans Logger.R. main.R contient le code d’entrée/sortie fourni et ne peut pas être modifié.

Essayez vous-même

source("Logger.R")

# Code d'entrée/sortie fourni : le garder tel quel
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
  p <- strsplit(line, ",")[[1]]
  if (p[1] == "error") log_error(p[2]) else log_info(p[2])
}
for (e in get_logger()$entries) cat(e, "\n", sep = "")
cat("errors: ", error_count(), "\n", sep = "")
cat("one logger: ", identical(get_logger(), get_logger()), "\n", sep = "")
quiz iconTestez-vous

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

Entraînez-vous par vous-même : Compilateur R en ligne