Menu
CoddyTech

Mở rộng với NextMethod

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 14 trên 57.

Một phương thức của lớp con thường làm công việc của lớp cha và thêm một chút nữa. NextMethod() gọi phương thức của lớp cha, nên lớp con chỉ bổ sung những gì mới. Lương tháng của người quản lý bằng lương của nhân viên cộng với tiền thưởng:

pay <- function(x) UseMethod("pay")
pay.employee <- function(x) x$salary / 12
pay.manager <- function(x) NextMethod() + x$bonus / 12
m <- structure(list(salary = 60000, bonus = 12000), class = c("manager", "employee"))
e <- structure(list(salary = 36000), class = "employee")
cat(pay(e), pay(m))

Đầu ra:

3000 6000

NextMethod() tiếp tục hoạt động qua nhiều cấp. Mỗi phương thức thêm phần của mình rồi chuyển tiếp đến lớp đứng sau lớp của nó:

pay <- function(x) UseMethod("pay")
pay.employee <- function(x) x$salary / 12
pay.manager <- function(x) NextMethod() + x$bonus / 12
pay.director <- function(x) NextMethod() + 500
d <- structure(list(salary = 120000, bonus = 24000),
               class = c("director", "manager", "employee"))
print(pay(d))

Đầu ra:

[1] 12500

Một phương thức lớp con cũng có thể kiểm tra trước và chỉ chuyển tiếp khi kiểm tra đạt yêu cầu. Phương thức của lớp cha trả về đối tượng đã thay đổi cùng với toàn bộ vector lớp của nó, vì vậy kết quả vẫn là một tài khoản tiết kiệm:

withdraw <- function(acc, amount) UseMethod("withdraw")
withdraw.account <- function(acc, amount) {
  if (amount > acc$balance) stop("insufficient funds", call. = FALSE)
  acc$balance <- acc$balance - amount
  acc
}
withdraw.savings <- function(acc, amount) {
  if (amount > 500) stop("savings limit is 500", call. = FALSE)
  NextMethod()
}
s <- structure(list(balance = 1000), class = c("savings", "account"))
s <- withdraw(s, 200)
cat(s$balance, class(s))

Đầu ra:

800 savings account

Hàm khởi tạo và phương thức dùng lại lớp cha theo cùng một cách: new_manager() gọi new_employee(), còn pay.manager() gọi NextMethod(). Code của nhân viên chỉ được viết một lần, và mỗi lớp con đều dựa trên code đó:

new_manager <- function(name, salary, bonus) {
  new_employee(name, salary, bonus = bonus, class = "manager")   # tái sử dụng hàm khởi tạo (constructor)
}
pay.manager <- function(x) NextMethod() + x$bonus / 12            # tái sử dụng phương thức (method)
challenge icon

Thử thách

Dễ

Tài khoản và tài khoản tiết kiệm nằm trong hai tệp. Trong Account.R, hãy hoàn thiện withdraw.account(acc, amount). Hàm này dừng với insufficient funds khi số tiền lớn hơn số dư, nếu không thì trả về tài khoản có số dư thấp hơn. Hãy hoàn thiện format.account(), hàm này trả về ada: 800. Trong Savings.R, hãy hoàn thiện withdraw.savings(). Hàm này dừng với savings limit is 500 khi số tiền lớn hơn 500, nếu không thì chuyển tiếp bằng NextMethod(). Hãy hoàn thiện format.savings(), hàm này trả về ada: 800 (savings) bằng cách gọi NextMethod().

Dùng call. = FALSE. Hàm tổng quát và các hàm khởi tạo đã được cung cấp. Code có sẵn đọc savings ada 1000 hoặc account bo 300, sau đó đọc các khoản tiền cần rút. Code in ra withdrew 200 hoặc refused: cùng thông báo, rồi cuối cùng in tài khoản đã định dạng.

Đặt code vào Account.R và Savings.R. Tệp 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("Account.R")
source("Savings.R")

# Đoạn code đầu vào/đầu ra có sẵn: giữ nguyên
input <- suppressWarnings(readLines(file("stdin")))
p <- strsplit(input[1], " ")[[1]]
acc <- if (p[1] == "savings") new_savings(p[2], as.numeric(p[3])) else new_account(p[2], as.numeric(p[3]))
for (amount in as.numeric(input[-1])) {
  acc <- tryCatch({
    acc <- withdraw(acc, amount)
    cat("withdrew ", amount, "\n", sep = "")
    acc
  }, error = function(e) {
    cat("refused: ", conditionMessage(e), "\n", sep = "")
    acc
  })
}
cat(format(acc), "\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