Menu
Coddy logo textTech

Singleton-Muster

Teil des Abschnitts Objektorientierte Programmierung der R-Journey von Coddy. Lektion 42 von 57.

Ein Entwurfsmuster ist eine bewährte Lösung für ein Entwurfsproblem, das immer wieder auftritt. Das Singleton-Muster stellt sicher, dass es genau eine Instanz von etwas gibt, die von überall aus erreichbar ist. In R erstellt eine Zugriffsfunktion die Instanz bei ihrem ersten Aufruf, bewahrt sie in einer mit local() erstellten Variable auf und gibt jedes Mal dieselbe zurück:

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

Ausgabe:

dark TRUE

Die Instanz ist eine Umgebung, daher wird eine Änderung, die durch einen Aufruf vorgenommen wurde, von jedem anderen Teil des Programms gesehen. Zwei Funktionen an unterschiedlichen Stellen schreiben in dasselbe Protokoll:

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)

Ausgabe:

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

Die Instanz wird erst erstellt, wenn sie zum ersten Mal benötigt wird. Das Zählen der Erstellungen zeigt, dass dies einmal geschieht, unabhängig davon, wie oft der Zugriff aufgerufen wird:

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

Ausgabe:

created 1 time(s)

Verwende Singletons sparsam. Ein Singleton ist ein globaler Zustand: Jeder Code kann ihn ändern, und ein Test kann nicht mit einer sauberen Kopie starten, ohne ihn zurückzusetzen. Er eignet sich für Dinge, die tatsächlich nur einmal pro Programm existieren, etwa einen Logger oder die Einstellungen; ein Objekt als Argument zu übergeben, ist oft klarer:

# klarer zum Testen: der log wird übergeben
process <- function(order, log) {
  log$lines <- c(log$lines, paste("processing", order$id))
}
challenge icon

Aufgabe

Einfach

Vervollständige den Singleton-Logger in Logger.R. get_logger() gibt eine Umgebung zurück, die beim ersten Aufruf erstellt wird und das Feld entries enthält (character, anfangs leer). Schreibe anschließend:

  • log_info(msg) und log_error(msg), die [INFO] msg oder [ERROR] msg zu den Einträgen des gemeinsam genutzten Loggers hinzufügen;
  • error_count(), das die Einträge zählt, die mit [ERROR] beginnen.

Der bereitgestellte Code liest Zeilen wie info,started oder error,disk full, protokolliert jede Zeile und gibt anschließend jeden Eintrag, die Anzahl der Fehler sowie aus, ob zwei Aufrufe von get_logger() dasselbe Objekt zurückgeben.

Dein Code gehört in Logger.R. main.R enthält den bereitgestellten Eingabe-/Ausgabecode und darf nicht bearbeitet werden.

Probier es selbst

source("Logger.R")

# Bereitgestellter Ein-/Ausgabe-Code: belasse ihn wie er ist
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 iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-R-Compiler