Padrão Builder
Parte da seção Programação Orientada a Objetos do Journey de R da Coddy. Lição 47 de 57.
Um builder monta um objeto complicado passo a passo e o cria no final com build(). Um builder baseado em closure mantém as partes em variáveis privadas; cada etapa retorna o próprio builder com invisible(self), para que as etapas possam ser encadeadas e uma etapa chamada sozinha não exiba nada:
pizza_builder <- function() {
size <- "medium"
toppings <- character(0)
self <- list(
size = function(s) { size <<- s; invisible(self) },
add = function(t) { toppings <<- c(toppings, t); invisible(self) },
build = function() structure(list(size = size, toppings = toppings), class = "pizza")
)
self
}
p <- pizza_builder()$size("large")$add("olives")$add("basil")$build()
cat(p$size, "with", paste(p$toppings, collapse = " and "))Saída:
large with olives and basilbuild() é o lugar para verificar se o objeto está completo. Partes ausentes interrompem a execução com uma mensagem em vez de produzir um objeto pela metade:
email_builder <- function() {
to <- NULL
subject <- ""
self <- list(
to = function(x) { to <<- x; invisible(self) },
subject = function(x) { subject <<- x; invisible(self) },
build = function() {
if (is.null(to)) stop("an email needs a recipient", call. = FALSE)
paste0("To: ", to, " | Subject: ", subject)
}
)
self
}
print(email_builder()$to("ada@x.org")$subject("Hi")$build())
print(tryCatch(email_builder()$subject("Hi")$build(), error = function(e) conditionMessage(e)))Saída:
[1] "To: ada@x.org | Subject: Hi"
[1] "an email needs a recipient"A mesma ideia funciona com valores simples e o pipe |>. Cada etapa é uma função que recebe a descrição criada até o momento e retorna uma cópia atualizada, e a última etapa constrói o resultado:
request <- function(url) list(url = url, method = "GET", headers = character(0))
with_method <- function(req, m) { req$method <- m; req }
with_header <- function(req, name, value) { req$headers[name] <- value; req }
build_request <- function(req) {
c(paste(req$method, req$url), paste0(names(req$headers), ": ", req$headers))
}
r <- request("/users") |> with_method("POST") |> with_header("Accept", "json") |> build_request()
cat(r, sep = "\n")Saída:
POST /users
Accept: jsonUm builder substitui um construtor com muitos argumentos opcionais. Cada etapa indica o que ela define, e as partes que não são mencionadas mantêm seus valores padrão:
# difícil de ler: o que é TRUE, o que é 3?
make_report("sales", TRUE, FALSE, 3, "pdf")
# um builder diz o que cada valor significa
report_builder("sales")$with_totals()$top(3)$format("pdf")$build()Desafio
FácilComplete o query_builder(table) baseado em closure em QueryBuilder.R. Suas etapas retornam o builder para que possam ser encadeadas:
select(cols): as colunas a serem exibidas (padrão*);where(cond): adiciona uma condição; várias são unidas comAND;order_by(col)elimit(n);build(): retorna a consulta, comoSELECT name, age FROM users WHERE age > 30 AND city = 'Rome' ORDER BY name LIMIT 5, omitindo as partes que não foram definidas.
O código fornecido lê a tabela, depois as linhas select name age, where age > 30, order name ou limit 5, e imprime a consulta construída. Em seguida, ele constrói uma segunda consulta em uma única cadeia.
Seu código deve ser colocado em QueryBuilder.R. main.R contém o código de entrada/saída fornecido e não pode ser editado.
Experimente você mesmo
source("QueryBuilder.R")
# Código de entrada/saída fornecido: mantenha-o como está
input <- suppressWarnings(readLines(file("stdin")))
q <- query_builder(input[1])
for (line in input[-1]) {
p <- strsplit(line, " ")[[1]]
rest <- paste(p[-1], collapse = " ")
if (p[1] == "select") q <- q$select(p[-1])
if (p[1] == "where") q <- q$where(rest)
if (p[1] == "order") q <- q$order_by(p[2])
if (p[1] == "limit") q <- q$limit(as.numeric(p[2]))
}
cat(q$build(), "\n", sep = "")
cat(query_builder("logs")$select("msg")$limit(1)$build(), "\n", sep = "")
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
1Fundamentos do S3
Trabalhando com ArquivosListas com uma ClasseFunções ConstrutorasMétodos de Impressão e FormataçãoValidadores e AuxiliaresRevisão - Temperaturas4Encapsulamento em R
Closures como ObjetosAmbientes como ObjetosFunções de AcessoProtegendo o EstadoRecapitulação - Parquímetro2Genéricos e Métodos S3
Genéricos e UseMethodMétodos padrãoMétodos para genéricos baseFundamentos de NextMethodRevisão - Áreas de formas5Classes S4
Definindo classes S4Slots e acessoVerificações de validadeMétodos showRevisão - Catálogo de produtos8Classes de Referência
Definindo Classes de ReferênciaMétodos e Atualizações de CamposSemântica de CópiaHerança e callSuperRecapitulação — Fila de Tarefas11Projeto: Gerenciamento de Biblioteca
Livros e MembrosEmpréstimo de LivrosPratique por conta própria: Compilador de R online