Patron Builder
Fait partie de la section Programmation orientée objet du Journey R de Coddy. Leçon 47 sur 57.
Un constructeur assemble un objet complexe étape par étape et le crée à la fin avec build(). Un constructeur basé sur une fermeture conserve les composants dans des variables privées chaque étape renvoie le constructeur lui-même avec invisible(self), ce qui permet d'enchaîner les étapes, et une étape appelée seule n'affiche rien :
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 "))Sortie :
large with olives and basilbuild() est l’endroit où vérifier que l’objet est complet. Les éléments manquants provoquent l’affichage d’un message au lieu de produire un objet à moitié construit :
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)))Sortie :
[1] "To: ada@x.org | Subject: Hi"
[1] "an email needs a recipient"La même idée fonctionne avec des valeurs simples et le pipe |>. Chaque étape est une fonction qui prend la description obtenue jusque-là et renvoie une copie mise à jour, et la dernière étape construit le résultat :
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")Sortie :
POST /users
Accept: jsonUn builder remplace un constructeur comportant de nombreux arguments facultatifs. Chaque étape indique ce qu’elle définit, et les éléments qui ne sont pas mentionnés conservent leurs valeurs par défaut :
# difficile à lire : qu'est-ce que TRUE, qu'est-ce que 3 ?
make_report("sales", TRUE, FALSE, 3, "pdf")
# un builder dit ce que chaque valeur signifie
report_builder("sales")$with_totals()$top(3)$format("pdf")$build()Défi
FacileComplétez le query_builder(table) basé sur une fermeture dans QueryBuilder.R. Ses étapes renvoient le générateur afin de pouvoir être enchaînées :
select(cols): les colonnes à afficher (par défaut*) ;where(cond): ajoute une condition ; plusieurs conditions sont reliées parAND;order_by(col)etlimit(n);build(): renvoie la requête, par exempleSELECT name, age FROM users WHERE age > 30 AND city = 'Rome' ORDER BY name LIMIT 5, en omettant les parties qui n'ont pas été définies.
Le code fourni lit la table, puis les lignes select name age, where age > 30, order name ou limit 5, et affiche la requête construite. Il construit ensuite une seconde requête en une seule chaîne.
Votre code doit être placé dans QueryBuilder.R. main.R contient le code d'entrée/sortie fourni et ne peut pas être modifié.
Essayez vous-même
source("QueryBuilder.R")
# Code d'entrée/sortie fourni : le garder tel quel
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 = "")
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation orientée objet
1Bases de S3
Travailler avec les fichiersListes avec une classeFonctions constructricesMéthodes print et formatValidateurs et fonctions utilitairesRécapitulatif - Températures4Encapsulation dans R
Les fermetures comme objetsLes environnements comme objetsFonctions d’accèsProtéger l’étatRécapitulatif - Parcmètre2S3 : génériques et méthodes
Génériques et UseMethodMéthodes par défautMéthodes pour les génériques de baseNotions de base sur NextMethodRécapitulatif - Aires des formes5Classes S4
Définir des classes S4Slots et accèsVérifications de validitéMéthodes showRécapitulatif - Catalogue de produits8Classes de référence
Définition des classes de référenceMéthodes et mises à jour des champsSémantique de copieHéritage et callSuperRécapitulatif - File d’attente des tâches11Projet : gestion de bibliothèque
Livres et membresEmprunter des livres3Héritage S3
Vecteurs de classesOrdre de recherche des méthodesÉtendre avec NextMethodVérifier avec inherits()Récapitulatif - Paie du personnelEntraînez-vous par vous-même : Compilateur R en ligne