الوراثة وcallSuper
جزء من قسم البرمجة كائنية التوجه في رحلة R على Coddy. الدرس 40 من 57.
contains = "Account" تجعل الفئة المرجعية ترث كل حقل وطريقة من 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التحدي
سهلالحسابات وحسابات التوفير هي Reference Classes في ملفين. أكمل الأساليب:
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 = "")
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات S3
التعامل مع الملفاتالقوائم باستخدام فئةدوال المُنشئأساليب الطباعة والتنسيقأدوات التحقق والمساعدةمراجعة - درجات الحرارة2S3 الدوال العامة والطرق
الدوال العامة وUseMethodالطرق الافتراضيةطرق الدوال العامة الأساسيةأساسيات NextMethodمراجعة - مساحات الأشكال8الأصناف المرجعية
تعريف الأصناف المرجعيةتحديث الأساليب والحقولدلالات النسخالوراثة وcallSuperمراجعة - قائمة المهامتدرّب بنفسك: مترجم R عبر الإنترنت