Mẫu Decorator
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 46 trên 57.
Decorator bao bọc một thứ và bổ sung hành vi mà vẫn giữ nguyên giao diện. Với một hàm, decorator nhận một hàm và trả về một hàm mới thực hiện thêm công việc xung quanh lời gọi:
with_logging <- function(f, name) {
function(x) {
cat("calling", name, "with", x, "\n")
f(x)
}
}
double <- function(x) x * 2
logged <- with_logging(double, "double")
print(logged(21))Đầu ra:
calling double with 21
[1] 42Một decorator có thể ghi nhớ các kết quả. with_cache() lưu mọi kết quả trong một môi trường và chỉ gọi hàm được bao bọc với các đối số mới:
with_cache <- function(f) {
cache <- new.env()
function(x) {
key <- as.character(x)
if (!exists(key, envir = cache, inherits = FALSE)) assign(key, f(x), envir = cache)
get(key, envir = cache)
}
}
work <- 0
slow_square <- function(x) {
work <<- work + 1
x^2
}
fast <- with_cache(slow_square)
for (x in c(3, 4, 3, 3, 4)) fast(x)
cat("results computed:", work)Đầu ra:
results computed: 2Đối tượng cũng có thể được trang trí. Mỗi decorator là một đối tượng chứa đối tượng mà nó bọc trong một trường và phản hồi cùng các generic, thêm phần của nó rồi gọi đối tượng bên trong xử lý phần còn lại:
cost <- function(d) UseMethod("cost")
describe <- function(d) UseMethod("describe")
cost.coffee <- function(d) 2
describe.coffee <- function(d) "coffee"
with_milk <- function(inner) structure(list(inner = inner), class = "milk")
cost.milk <- function(d) cost(d$inner) + 0.5
describe.milk <- function(d) paste(describe(d$inner), "+ milk")
cup <- with_milk(with_milk(structure(list(), class = "coffee")))
cat(describe(cup), "=", cost(cup))Đầu ra:
coffee + milk + milk = 3Các hàm trang trí có thể lồng nhau, và thứ tự của chúng rất quan trọng: hàm ngoài cùng chạy đầu tiên trên đường vào và cuối cùng trên đường ra:
exclaim <- function(f) function(s) paste0(f(s), "!")
twice <- function(f) function(s) paste(f(s), f(s))
greet <- function(name) paste("hi", name)
a <- twice(exclaim(greet))
b <- exclaim(twice(greet))
cat(a("ada"), "/", b("ada"))Đầu ra:
hi ada! hi ada! / hi ada hi ada!Thử thách
DễĐồ uống được trang trí bằng các món thêm. Drink.R chứa các hàm tổng quát cost() và describe(), cùng với đồ uống cơ bản new_coffee(), có giá 2 và tự mô tả là coffee. Trong Extras.R, hãy viết ba hàm trang trí, mỗi hàm là một đối tượng chứa đồ uống mà nó bọc trong trường inner:
add_milk(d): lớpmilk, thêm 0.50 và+ milk;add_sugar(d): lớpsugar, thêm 0.20 và+ sugar;add_syrup(d, flavour): lớpsyrupcó trườngflavour, thêm 0.80 và+ vanilla syrup.
Viết các phương thức cost() và describe() của chúng bằng cách gọi đồ uống bên trong trước. Code được cung cấp đọc các dòng như milk,syrup vanilla,sugar (dòng trống có nghĩa là cà phê nguyên chất) và in ra coffee + milk + vanilla syrup + sugar: 3.50.
Code của bạn nằm trong Drink.R và Extras.R. main.R chứa code đầu vào/đầu ra được cung cấp và không thể chỉnh sửa.
Tự làm thử
source("Drink.R")
source("Extras.R")
# Đoạn code đầu vào/đầu ra đã cho: giữ nguyên
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
d <- new_coffee()
if (nchar(line) > 0) for (extra in strsplit(line, ",")[[1]]) {
p <- strsplit(extra, " ")[[1]]
d <- switch(p[1], milk = add_milk(d), sugar = add_sugar(d), syrup = add_syrup(d, p[2]))
}
cat(sprintf("%s: %.2f", describe(d), cost(d)), "\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