Menu
Coddy logo textTech

Padrão Singleton

Parte da seção Programação Orientada a Objetos do Journey de R da Coddy. Lição 42 de 57.

Um padrão de projeto é uma solução comprovada para um problema de projeto que surge repetidamente. O padrão singleton garante que exista exatamente uma instância de algo, acessível de qualquer lugar. Em R, uma função acessora cria a instância em sua primeira chamada, mantém-na em uma variável criada com local() e retorna a mesma todas as vezes:

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

Saída:

dark TRUE

A instância é um ambiente, portanto uma alteração feita por meio de uma chamada é vista por todas as outras partes do programa. Duas funções em lugares diferentes escrevem no mesmo 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)

Saída:

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

A instância é criada somente quando é necessária pela primeira vez. Contar as criações mostra que isso acontece uma vez, independentemente de quantas vezes o acessador é chamado:

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

Saída:

created 1 time(s)

Use singletons com moderação. Um singleton é um estado global: qualquer código pode alterá-lo, e um teste não pode começar com uma cópia limpa sem redefini-lo. Ele é adequado para coisas que realmente existem uma vez por programa, como um logger ou as configurações; passar um objeto como argumento costuma ser mais claro:

# mais claro para testar: o log é passado
process <- function(order, log) {
  log$lines <- c(log$lines, paste("processing", order$id))
}
challenge icon

Desafio

Fácil

Complete o logger singleton em Logger.R. get_logger() retorna um ambiente, criado na primeira chamada, com o campo entries (de caracteres, inicialmente vazio). Em seguida, escreva:

  • log_info(msg) e log_error(msg), que adicionam [INFO] msg ou [ERROR] msg às entradas do logger compartilhado;
  • error_count(), que conta as entradas que começam com [ERROR].

O código fornecido lê linhas info,started ou error,disk full, registra cada uma, depois imprime todas as entradas, o número de erros e se duas chamadas de get_logger() retornam o mesmo objeto.

Seu código deve ser colocado em Logger.R. main.R contém o código fornecido de entrada/saída e não pode ser editado.

Experimente você mesmo

source("Logger.R")

# Código de entrada/saída fornecido: mantenha-o 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 iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos

Pratique por conta própria: Compilador de R online