Mẫu Builder
Thuộc phần Lập trình hướng đối tượng trong hành trình R của Coddy. Bài học 47 trên 57.
Một builder tạo một đối tượng phức tạp theo từng bước và hoàn tất việc tạo đối tượng ở cuối bằng build(). Builder dựa trên closure giữ các phần trong biến riêng tư; mỗi bước trả về chính builder bằng invisible(self), nhờ đó các bước có thể nối tiếp nhau và một bước được gọi riêng sẽ không in ra gì:
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 "))Đầu ra:
large with olives and basilbuild() là nơi kiểm tra đối tượng đã hoàn chỉnh chưa. Nếu thiếu phần nào, chương trình sẽ dừng và hiển thị thông báo thay vì tạo ra một đối tượng chưa hoàn chỉnh:
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)))Đầu ra:
[1] "To: ada@x.org | Subject: Hi"
[1] "an email needs a recipient"Ý tưởng tương tự cũng áp dụng với các giá trị thông thường và toán tử pipe |>. Mỗi bước là một hàm nhận mô tả hiện tại và trả về một bản sao đã cập nhật, còn bước cuối cùng tạo ra kết quả:
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")Đầu ra:
POST /users
Accept: jsonMột builder thay thế hàm khởi tạo có nhiều đối số tùy chọn. Mỗi bước nêu rõ giá trị mà nó thiết lập, còn những phần không được nhắc đến sẽ giữ nguyên giá trị mặc định:
# Khó hiểu: TRUE và 3 có nghĩa là gì?
make_report("sales", TRUE, FALSE, 3, "pdf")
# Builder cho biết ý nghĩa của từng giá trị (value)
report_builder("sales")$with_totals()$top(3)$format("pdf")$build()Thử thách
DễHoàn thiện query_builder(table) dựa trên closure trong QueryBuilder.R. Các bước của hàm trả về bộ dựng để bạn có thể nối chuỗi lệnh:
select(cols): các cột cần hiển thị (mặc định là*);where(cond): thêm một điều kiện; nếu có nhiều điều kiện, hãy nối chúng bằngAND;order_by(col)vàlimit(n);build(): trả về truy vấn, chẳng hạnSELECT name, age FROM users WHERE age > 30 AND city = 'Rome' ORDER BY name LIMIT 5, đồng thời bỏ qua những phần chưa được thiết lập.
Code được cung cấp đọc bảng, sau đó đọc các dòng select name age, where age > 30, order name hoặc limit 5, rồi in ra truy vấn đã dựng. Tiếp đó, code dựng truy vấn thứ hai bằng một chuỗi lệnh.
Code của bạn nằm trong QueryBuilder.R. main.R chứa code đầu vào/đầu ra được cung cấp và bạn không thể chỉnh sửa tệp này.
Tự làm thử
source("QueryBuilder.R")
# Đoạn code đầu vào/đầu ra đã cho: giữ nguyên
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 = "")
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Kiến thức cơ bản về S3
Làm việc với tệpDanh sách với một lớpHàm khởi tạoPhương thức in ra và định dạngTrình xác thực và hàm hỗ trợÔn tập: nhiệt độ4Tính đóng gói trong R
Closure dưới dạng đối tượngMôi trường dưới dạng đối tượngHàm truy cậpBảo vệ trạng tháiÔn tập: đồng hồ tính phí đỗ xe2S3, hàm generic và phương thức
Hàm generic và UseMethodPhương thức mặc địnhPhương thức cho hàm generic cơ sởCơ bản về NextMethodÔn tập: diện tích hình5Các lớp S4
Định nghĩa lớp S4Slot và quyền truy cậpKiểm tra tính hợp lệPhương thức showÔn tập: Danh mục sản phẩmTự luyện tập: Trình biên dịch R online