Pattern Builder
Fa parte della sezione Programmazione orientata agli oggetti del percorso R di Coddy. Lezione 47 di 57.
Un builder assembla un oggetto complesso passo dopo passo e lo crea alla fine con build(). Un builder basato su closure mantiene le parti in variabili private; ogni passaggio restituisce il builder stesso con invisible(self), così i passaggi possono essere concatenati e un passaggio chiamato da solo non stampa nulla:
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 "))Output:
large with olives and basilbuild() è il punto in cui verificare che l'oggetto sia completo. Se mancano delle parti, viene visualizzato un messaggio invece di produrre un oggetto incompleto:
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)))Risultato:
[1] "To: ada@x.org | Subject: Hi"
[1] "an email needs a recipient"La stessa idea funziona con valori semplici e la pipe |>. Ogni passaggio è una funzione che prende la descrizione costruita finora e restituisce una copia aggiornata, e l’ultimo passaggio crea il risultato:
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")Output:
POST /users
Accept: jsonUn builder sostituisce un costruttore con molti argomenti facoltativi. Ogni passaggio indica cosa imposta, mentre le parti non menzionate mantengono i valori predefiniti:
# difficile da leggere: cosa significa TRUE, cosa significa 3?
make_report("sales", TRUE, FALSE, 3, "pdf")
# un builder indica il significato di ogni valore
report_builder("sales")$with_totals()$top(3)$format("pdf")$build()Sfida
FacileCompleta il query_builder(table) basato su chiusure. I suoi passaggi restituiscono il builder, così possono essere concatenati:
select(cols): le colonne da mostrare (predefinito*);where(cond): aggiunge una condizione; più condizioni vengono unite conAND;order_by(col)elimit(n);build(): restituisce la query, ad esempioSELECT name, age FROM users WHERE age > 30 AND city = 'Rome' ORDER BY name LIMIT 5, omettendo le parti che non sono state impostate.
Il codice fornito legge la tabella, poi le righe select name age, where age > 30, order name o limit 5, e stampa la query creata. Quindi crea una seconda query concatenando le chiamate in un'unica catena.
Il tuo codice va in QueryBuilder.R. main.R contiene il codice di input/output fornito e non può essere modificato.
Provalo tu
source("QueryBuilder.R")
# Codice di input/output fornito: lascialo così com'è
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 = "")
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
1Nozioni di base su S3
Lavorare con i fileListe con una classeFunzioni costruttriciMetodi di stampa e formattazioneValidatori e funzioni di supportoRipasso - Temperature4Incapsulamento in R
Chiusure come oggettiAmbienti come oggettiFunzioni di accessoProteggere lo statoRiepilogo - Parcometro2Generics e metodi S3
Generics e UseMethodMetodi predefinitiMetodi per i generics di baseNozioni di base su NextMethodRiepilogo - Aree delle forme5Classi S4
Definire le classi S4Slot e accessoControlli di validitàMetodi showRiepilogo - Catalogo prodotti8Classi di riferimento
Definizione delle classi di riferimentoMetodi e aggiornamenti dei campiSemantica della copiaEreditarietà e callSuperRiepilogo - coda di attività11Progetto: Gestione della biblioteca
Libri e iscrittiPrestito dei libriEsercitati da solo: Compilatore R online