Расширение с помощью NextMethod
Часть раздела Объектно-ориентированное программирование путешествия по R на Coddy. Урок 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 = "")
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы S3
Работа с файламиСписки с классомФункции-конструкторыМетоды print и formatВалидаторы и вспомогательные функцииПовторение — температуры4Инкапсуляция в R
Замыкания как объектыОкружения как объектыФункции доступаЗащита состоянияПовторение — парковочный счётчик7S4 Наследование
Расширение с помощью containscallNextMethodВиртуальные классыПроверка типов с помощью is()Повторение — смотритель зоопарка10Шаблоны проектирования, часть 2
Шаблон «Декоратор»Шаблон «Строитель»Шаблон «Состояние»Шаблон «Итератор»2Обобщённые функции и методы S3
Обобщённые функции и UseMethodМетоды по умолчаниюМетоды для базовых обобщённых функцийОсновы NextMethodПовторение — площади фигур5Классы S4
Определение классов S4Слоты и доступПроверка корректностиМетоды showПовторение — каталог товаров8Ссылочные классы
Определение ссылочных классовМетоды и обновление полейСемантика копированияНаследование и callSuperПовторение — очередь задач11Проект: Управление библиотекой
Книги и читателиВыдача книг3Наследование S3
Векторы классовПорядок поиска методовРасширение с помощью NextMethodПроверка с помощью inherits()Повторение — расчёт зарплаты сотрудниковПотренируйтесь самостоятельно: Онлайн-компилятор R