Menu
Coddy logo textTech

Pattern Singleton

Fa parte della sezione Programmazione orientata agli oggetti del percorso R di Coddy. Lezione 42 di 57.

Un pattern di progettazione è una soluzione collaudata a un problema di progettazione che si ripresenta più e più volte. Il pattern singleton garantisce che esista esattamente un’istanza di qualcosa, accessibile da qualsiasi punto. In R, una funzione di accesso crea l’istanza alla prima chiamata, la conserva in una variabile creata con local() e restituisce la stessa ogni volta:

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

Output:

dark TRUE

L'istanza è un ambiente, quindi una modifica effettuata tramite una chiamata viene vista da ogni altra parte del programma. Due funzioni in punti diversi scrivono nello stesso log:

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)

Output:

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

L'istanza viene creata solo quando serve per la prima volta. Contando le creazioni, si vede che avviene una sola volta, indipendentemente da quante volte viene chiamato l'accessore:

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

Output:

created 1 time(s)

Usa i singleton con moderazione. Un singleton è uno stato globale: qualsiasi codice può modificarlo e un test non può partire da una copia pulita senza ripristinarlo. È adatto a ciò che è davvero unico per programma, come un logger o le impostazioni; passare un oggetto come argomento è spesso più chiaro:

# Più facile da testare: il log viene passato come argomento
process <- function(order, log) {
  log$lines <- c(log$lines, paste("processing", order$id))
}
challenge icon

Sfida

Facile

Completa il logger singleton in Logger.R. get_logger() restituisce un ambiente, creato alla prima chiamata, con il campo entries (carattere, inizialmente vuoto). Poi scrivi:

  • log_info(msg) e log_error(msg), che aggiungono [INFO] msg o [ERROR] msg alle voci del logger condiviso;
  • error_count(), che conta le voci che iniziano con [ERROR].

Il codice fornito legge righe info,started o error,disk full, registra ciascuna, poi stampa ogni voce, il numero di errori e indica se due chiamate a get_logger() restituiscono lo stesso oggetto.

Il tuo codice va in Logger.R. main.R contiene il codice di input/output fornito e non può essere modificato.

Provalo tu

source("Logger.R")

# Código de entrada/salida proporcionado: mantenlo 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 iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore R online