Menu
Coddy logo textTech

הרחבה באמצעות 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 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

אתגר

קל

חשבונות וחשבונות חיסכון נמצאים בשני קבצים. ב־Account.R השלימו את withdraw.account(acc, amount), שעוצרת עם insufficient funds כשהסכום גבוה מהיתרה, ואחרת מחזירה את החשבון עם יתרה נמוכה יותר, ואת format.account(), שמחזירה ada: 800. ב־Savings.R השלימו את withdraw.savings(), שעוצרת עם savings limit is 500 עבור סכומים מעל 500, ואחרת מעבירה את הטיפול הלאה באמצעות NextMethod(), ואת format.savings(), שמחזירה ada: 800 (savings) באמצעות קריאה ל־NextMethod().

השתמשו ב־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 אונליין