Menu
Coddy logo textTech

Extending With NextMethod

Part of the Object Oriented Programming section of Coddy's R journey. Lesson 14 of 57.

A child method often does the parent's work plus a little more. NextMethod() calls the parent method, so the child adds only what is new. A manager's monthly pay is an employee's pay plus the bonus:

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))

Output:

3000 6000

NextMethod() keeps working down several levels. Each method adds its part and hands on to the class after its own:

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))

Output:

[1] 12500

A child method can also check first and hand on only when the check passes. The parent's method returns the changed object with its full class vector, so the result is still a savings account:

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))

Output:

800 savings account

Constructors and methods reuse the parent in the same way: new_manager() calls new_employee(), and pay.manager() calls NextMethod(). The employee code is written once and every subclass builds on it:

new_manager <- function(name, salary, bonus) {
  new_employee(name, salary, bonus = bonus, class = "manager")   # reuse the constructor
}
pay.manager <- function(x) NextMethod() + x$bonus / 12            # reuse the method
challenge icon

Challenge

Easy

Accounts and savings accounts are in two files. In Account.R complete withdraw.account(acc, amount), which stops with insufficient funds when the amount is above the balance and otherwise returns the account with the lower balance, and format.account(), which returns ada: 800. In Savings.R complete withdraw.savings(), which stops with savings limit is 500 for amounts above 500 and otherwise hands on with NextMethod(), and format.savings(), which returns ada: 800 (savings) by calling NextMethod().

Use call. = FALSE. The generic and constructors are given. The supplied code reads savings ada 1000 or account bo 300, then amounts to withdraw; it prints withdrew 200 or refused: and the message, and finally the formatted account.

Your code goes in Account.R and Savings.R. main.R holds the supplied input/output code and cannot be edited.

Try it yourself

source("Account.R")
source("Savings.R")

# Supplied input/output code: keep it as it is
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 iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming

Practice on your own: Online R compiler