source(file.path(usethis::proj_get(), "vignettes", "_common.R"))
cat(knitr::knit_child('./vignettes/details/_Singleton.Rmd', quiet = TRUE))

Example: Counter {#example-1}

In this example we implement a Counter that inherits the qualities of Singleton.

Counter <- R6::R6Class(inherit = Singleton, public = list(
  count = 0,
  add_1 = function() {
    self$count <- self$count + 1
    invisible(self)
  }
))

Whenever we call the constructor on Counter, we always get the exact same instance:

counter_A <- Counter$new()
counter_B <- Counter$new()

identical(counter_A, counter_B, ignore.environment = FALSE)

The two objects are equal and located at the same address; thus, they are the same object.

When we make a change in any of the class instances, the rest of the instances are changed as well.

# How many times has the counter been increased?
counter_A$count

# Increase the counter by 1
counter_A$add_1()

# How many times have the counters been increased?
counter_A$count
counter_B$count

Example: Data Transfer Object (DTO)

In this example, we crate access to a database that only establishes one connection per session.

Notice how the initialize public method first calls the initialize of the Singleton, and then appends its own initialisation routine.

DTO <- R6::R6Class(classname = "DTO", inherit = Singleton, public = list(
  con = NULL,
  initialize = function() {
    super$initialize()
    self$establish_connection()
  },
  establish_connection = function() {
    self$con <- DBI::dbConnect(RSQLite::SQLite(), dbname = ":memory:")
    return(self)
  },
  dbSendQuery = function(statement) {
    res <- DBI::dbSendQuery(self$con, statement)
    return(res)
  }
))

Once a database connection has established during the session, other calls to DTO instantiate objects that use the same database connection.

database_A <- DTO$new()
database_B <- DTO$new()
identical(database_A, database_B)

Further Reading

Singleton on Wikipedia



tidylab/R6P documentation built on Dec. 23, 2024, 9:22 a.m.