使用 NextMethod 扩展
本课属于 Coddy R 学习旅程的“面向对象编程”单元。第 14 课,共 57 课。
子类方法通常会完成父类的工作,再多做一点。NextMethod() 会调用父类方法,因此子类只需添加新的内容。经理的月薪等于员工的工资加上奖金:
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))输出:
3000 6000NextMethod() 会继续向下逐层调用。每个方法都会添加自己的部分,并将处理交给它之后的类:
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))输出:
[1] 12500子类方法也可以先进行检查,只有检查通过后才交给父类方法处理。父类的方法会返回修改后的对象,并保留完整的类向量,所以结果仍然是一个储蓄账户:
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))输出:
800 savings account构造函数和方法以相同的方式复用父类:new_manager() 调用 new_employee(),而 pay.manager() 调用 NextMethod()。员工代码只需编写一次,每个子类都在此基础上构建:
new_manager <- function(name, salary, bonus) {
new_employee(name, salary, bonus = bonus, class = "manager") # 复用构造函数
}
pay.manager <- function(x) NextMethod() + x$bonus / 12 # 复用方法挑战
简单账户和储蓄账户分别放在两个文件中。在 Account.R 中完成 withdraw.account(acc, amount):当金额高于余额时,使用 insufficient funds 停止;否则返回余额较低的账户。还要完成 format.account(),使其返回 ada: 800。在 Savings.R 中完成 withdraw.savings():当金额高于 500 时,使用 savings limit is 500 停止;否则调用 NextMethod() 继续处理。还要完成 format.savings(),通过调用 NextMethod() 返回 ada: 800 (savings)。
使用 call. = FALSE。泛型函数和构造函数已提供。给定的代码会读取 savings ada 1000 或 account bo 300,然后读取要取出的金额;它会输出 withdrew 200 或 refused: 和错误消息,最后输出格式化后的账户。
你的代码要写在 Account.R 和 Savings.R 中。main.R 存放给定的输入/输出代码,不能编辑。
亲手试试
source("Account.R")
source("Savings.R")
# 提供的输入和输出代码:保持原样。
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 = "")
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: R 在线编译器