Wzorzec Budowniczy
Część sekcji Programowanie obiektowe ścieżki R w Coddy. Lekcja 47 z 57.
Konstruktor składa złożony obiekt krok po kroku i tworzy go na końcu za pomocą build(). Konstruktor oparty na domknięciu przechowuje części w prywatnych zmiennych; każdy krok zwraca sam konstruktor za pomocą invisible(self), dzięki czemu kroki można łączyć w łańcuch, a samodzielne wywołanie kroku niczego nie wypisuje:
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 "))Wynik:
large with olives and basilbuild() to miejsce, w którym należy sprawdzić, czy obiekt jest kompletny. Brakujące elementy powodują zatrzymanie działania z komunikatem zamiast utworzenia niekompletnego obiektu:
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)))Wynik:
[1] "To: ada@x.org | Subject: Hi"
[1] "an email needs a recipient"Ta sama idea działa w przypadku zwykłych wartości i operatora potoku |>. Każdy krok to funkcja, która przyjmuje dotychczasowy opis i zwraca jego zaktualizowaną kopię, a ostatni krok tworzy wynik:
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")Wynik:
POST /users
Accept: jsonKonstruktor z wieloma opcjonalnymi argumentami można zastąpić konstruktorem budującym obiekt. Każdy krok określa, co ustawia, a pominięte elementy zachowują wartości domyślne:
# trudne do odczytania: co oznacza TRUE, a co 3?
make_report("sales", TRUE, FALSE, 3, "pdf")
# builder wyjaśnia, co oznacza każda wartość
report_builder("sales")$with_totals()$top(3)$format("pdf")$build()Wyzwanie
ŁatwyUzupełnij oparty na domknięciach query_builder(table) w pliku QueryBuilder.R. Jego kroki zwracają obiekt buildera, dzięki czemu można je łączyć w łańcuch:
select(cols): kolumny do wyświetlenia (domyślnie*);where(cond): dodaje warunek; kilka warunków łączy się za pomocąAND;order_by(col)ilimit(n);build(): zwraca zapytanie, takie jakSELECT name, age FROM users WHERE age > 30 AND city = 'Rome' ORDER BY name LIMIT 5, pomijając części, których nie ustawiono.
Dostarczony kod odczytuje tabelę, a następnie wiersze select name age, where age > 30, order name lub limit 5 i wypisuje zbudowane zapytanie. Następnie buduje drugie zapytanie w jednym łańcuchu.
Twój kod umieść w pliku QueryBuilder.R. Plik main.R zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.
Spróbuj swoich sił
source("QueryBuilder.R")
# Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
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 = "")
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy S3
Praca z plikamiListy z klasąFunkcje konstruktoraMetody print i formatWalidatory i funkcje pomocniczePowtórzenie — temperatury4Enkapsulacja w R
Domknięcia jako obiektyŚrodowiska jako obiektyFunkcje dostępoweOchrona stanuPodsumowanie – parkometr2Generics i metody S3
Generics i UseMethodMetody domyślneMetody dla podstawowych genericsPodstawy NextMethodPowtórzenie — pola figur5Klasy S4
Definiowanie klas S4Sloty i dostępSprawdzanie poprawnościMetody wyświetlaniaPodsumowanie – katalog produktów8Klasy referencyjne
Definiowanie klas referencyjnychMetody i aktualizacja pólSemantyka kopiowaniaDziedziczenie i callSuperPodsumowanie – kolejka zadań11Projekt: Zarządzanie biblioteką
Książki i czytelnicyWypożyczanie książek3Dziedziczenie S3
Wektory klasKolejność wyszukiwania metodRozszerzanie za pomocą NextMethodSprawdzanie za pomocą inherits()Powtórzenie — lista płac pracownikówPoćwicz samodzielnie: Kompilator R online