Menu
CoddyTech

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] 42

Mộ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 = 3

Cá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!
challenge icon

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ớp milk, thêm 0.50 và + milk;
  • add_sugar(d): lớp sugar, thêm 0.20 và + sugar;
  • add_syrup(d, flavour): lớp syrup có trường flavour, 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 = "")
}
quiz iconTự kiểm tra

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

Tự luyện tập: Trình biên dịch R online