Menu
Coddy logo textTech

ירושה ו־callSuper

חלק מהיחידה תכנות מונחה עצמים במסלול ה-R של Coddy. שיעור 40 מתוך 57.

contains = "Account" גורם ל-Reference Class לרשת כל שדה ומתודה של Account. מחלקת המשנה מוסיפה שדות ומתודות משלה:

Account <- suppressMessages(setRefClass("Account",
  fields = list(owner = "character", balance = "numeric"),
  methods = list(deposit = function(x) balance <<- balance + x)
))
Savings <- suppressMessages(setRefClass("Savings", contains = "Account",
  fields = list(rate = "numeric"),
  methods = list(add_interest = function() deposit(balance * rate))
))
s <- Savings$new(owner = "Ada", balance = 100, rate = 0.05)
s$deposit(100)
s$add_interest()
cat(s$balance, is(s, "Account"))

פלט:

210 TRUE

מחלקת־בת דורסת מתודה באמצעות הגדרת מתודה באותו שם. בתוכה, callSuper(...) קוראת לגרסה של מחלקת־האב, כך שהמחלקה־הבת רק מוסיפה את מה שחדש:

Account <- suppressMessages(setRefClass("Account",
  fields = list(balance = "numeric"),
  methods = list(withdraw = function(x) {
    if (x > balance) stop("insufficient funds", call. = FALSE)
    balance <<- balance - x
  })
))
Savings <- suppressMessages(setRefClass("Savings", contains = "Account",
  methods = list(withdraw = function(x) {
    if (x > 500) stop("limit is 500", call. = FALSE)
    callSuper(x)
  })
))
s <- Savings$new(balance = 300)
s$withdraw(200)
for (x in c(600, 400)) cat(tryCatch({ s$withdraw(x); "ok" }, error = function(e) conditionMessage(e)), "\n")
cat(s$balance, "\n")

פלט:

limit is 500 
insufficient funds 
100 

initialize עובר בירושה ונדרסת בדומה לכל מתודה. המתודה initialize של מחלקת הבת קוראת ל-callSuper(...) כדי לאפשר למחלקת האב להגדיר את השדות שלה, ואז מגדירה את ערכי ברירת המחדל שלה:

Account <- suppressMessages(setRefClass("Account",
  fields = list(owner = "character", balance = "numeric"),
  methods = list(initialize = function(...) {
    initFields(...)
    if (length(balance) == 0) balance <<- 0
    invisible(.self)
  })
))
Savings <- suppressMessages(setRefClass("Savings", contains = "Account",
  fields = list(rate = "numeric"),
  methods = list(initialize = function(...) {
    callSuper(...)
    if (length(rate) == 0) rate <<- 0.01
    invisible(.self)
  })
))
s <- Savings$new(owner = "Bo")
cat(s$owner, s$balance, s$rate)

פלט:

Bo 0 0.01

אובייקטים של תת־מחלקות שונות מגיבים לאותה קריאה למתודה בדרכם שלהם, ולכן לולאה על רשימה מעורבת אינה זקוקה לבדיקות של המחלקה:

Account <- suppressMessages(setRefClass("Account",
  fields = list(balance = "numeric"),
  methods = list(month_end = function() balance <<- balance - 2)
))
Savings <- suppressMessages(setRefClass("Savings", contains = "Account",
  methods = list(month_end = function() balance <<- balance * 1.01)
))
bank <- list(Account$new(balance = 100), Savings$new(balance = 100))
for (a in bank) a$month_end()
cat(vapply(bank, function(a) a$balance, numeric(1)))

פלט:

98 101
challenge icon

אתגר

קל

חשבונות וחשבונות חיסכון הם מחלקות Reference בשני קבצים. השלימו את המתודות:

  • Account בקובץ Account.R (השדות owner, balance): initialize מגדירה את היתרה ל־0 כאשר היא לא ניתנת; deposit(x); withdraw(x) עוצרת עם insufficient funds; month_end() גובה עמלה של 1; describe() מחזירה ada: 99.
  • Savings בקובץ Savings.R (מכילה את Account, השדה rate): withdraw(x) עוצרת עם limit is 500 כאשר הסכום גדול מ־500, ובשאר המקרים קוראת ל־callSuper(x); month_end() מוסיפה ריבית, balance * rate, במקום העמלה; describe() מוסיפה (savings) באמצעות callSuper().

השתמשו ב־call. = FALSE. שתי ההגדרות עטופות ב־suppressMessages(). הקוד שסופק קורא savings ada 0.1 או account bo, ולאחר מכן dep 100, wd 30 או month, ומדפיס describe() או את 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") Savings$new(owner = p[2], rate = as.numeric(p[3])) else Account$new(owner = p[2])
for (line in input[-1]) {
  q <- strsplit(line, " ")[[1]]
  msg <- tryCatch({
    if (q[1] == "dep") acc$deposit(as.numeric(q[2]))
    if (q[1] == "wd") acc$withdraw(as.numeric(q[2]))
    if (q[1] == "month") acc$month_end()
    acc$describe()
  }, error = function(e) paste("refused:", conditionMessage(e)))
  cat(line, ": ", msg, "\n", sep = "")
}
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר R אונליין