Menu
Coddy logo textTech

Patrón Singleton

Parte de la sección Programación orientada a objetos del Journey de R de Coddy. Lección 42 de 57.

Un patrón de diseño es una solución comprobada para un problema de diseño que surge una y otra vez. El patrón singleton garantiza que exista exactamente una instancia de algo, accesible desde cualquier lugar. En R, una función de acceso crea la instancia en su primera llamada, la conserva en una variable creada con local() y devuelve la misma cada vez:

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))

Salida:

dark TRUE

La instancia es un entorno, por lo que un cambio realizado mediante una llamada es visible para cualquier otra parte del programa. Dos funciones en lugares diferentes escriben en el mismo registro:

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)

Salida:

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

La instancia se crea únicamente cuando se necesita por primera vez. Contar las creaciones muestra que ocurre una vez, sin importar con qué frecuencia se llame al accessor:

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)")

Salida:

created 1 time(s)

Usa los singletons con moderación. Un singleton es un estado global: cualquier código puede cambiarlo, y una prueba no puede comenzar con una copia limpia sin restablecerlo. Es adecuado para cosas que realmente son únicas por programa, como un logger o la configuración; pasar un objeto como argumento suele ser más claro:

# más claro para probar: el log se pasa
process <- function(order, log) {
  log$lines <- c(log$lines, paste("processing", order$id))
}
challenge icon

Desafío

Fácil

Completa el registrador singleton en Logger.R. get_logger() devuelve un entorno, creado en la primera llamada, con el campo entries (de tipo character, vacío al principio). Después escribe:

  • log_info(msg) y log_error(msg), que añaden [INFO] msg o [ERROR] msg a las entradas del registrador compartido;
  • error_count(), que cuenta las entradas que comienzan con [ERROR].

El código proporcionado lee líneas info,started o error,disk full, registra cada una y después imprime todas las entradas, el número de errores y si dos llamadas a get_logger() devuelven el mismo objeto.

Tu código debe estar en Logger.R. main.R contiene el código proporcionado de entrada/salida y no se puede editar.

Pruébalo tú mismo

source("Logger.R")

# Código de entrada/salida suministrado: déjalo tal como está
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 iconPonte a prueba

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

Practica por tu cuenta: Compilador de R online