Menu
Coddy logo textTech

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 6000

NextMethod() は複数のレベルにわたって処理を続けます。各メソッドは自身の処理を追加し、自分の次のクラスへ処理を引き渡します。

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            # メソッドを再利用する
challenge icon

チャレンジ

簡単

アカウントと貯蓄アカウントは2つのファイルに分かれています。Account.Rでは、金額が残高を上回る場合にinsufficient fundsで停止し、それ以外の場合は残高が減ったアカウントを返すwithdraw.account(acc, amount)と、ada: 800を返すformat.account()を完成させてください。Savings.Rでは、金額が500を上回る場合にsavings limit is 500で停止し、それ以外の場合はNextMethod()に処理を引き渡すwithdraw.savings()と、NextMethod()を呼び出してada: 800 (savings)を返すformat.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 = "")
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Rオンラインコンパイラ