حماية الحالة
جزء من قسم البرمجة كائنية التوجه في رحلة R على Coddy. الدرس 20 من 57.
دالة الضبط هي المكان الوحيد الذي يمكن أن تتغير فيه قيمة، لذا فهي المكان المناسب لفرض القواعد. فهي تتحقق أولًا وتستدعي stop() قبل تغيير أي شيء، ويبقى الكائن صالحًا:
make_account <- function(balance = 0) {
withdraw <- function(x) {
if (x <= 0) stop("amount must be positive", call. = FALSE)
if (x > balance) stop("insufficient funds", call. = FALSE)
balance <<- balance - x
}
list(withdraw = withdraw, balance = function() balance)
}
acc <- make_account(50)
msg <- tryCatch(acc$withdraw(80), error = function(e) conditionMessage(e))
cat(msg, "/", acc$balance())الناتج:
insufficient funds / 50lockBinding("rate", e) يجعل متغيرًا واحدًا في البيئة للقراءة فقط. ويؤدي إسناد قيمة إليه بعد ذلك إلى حدوث خطأ، بينما تظل المتغيرات الأخرى قابلة للكتابة:
loan <- new.env()
loan$rate <- 0.05
loan$owed <- 1000
lockBinding("rate", loan)
loan$owed <- 900
msg <- tryCatch({ loan$rate <- 0; "changed" }, error = function(e) conditionMessage(e))
cat(msg, "\n")
cat(loan$rate, loan$owed, "\n")الناتج:
cannot change value of locked binding for 'rate'
0.05 900 lockEnvironment(e) يمنع إضافة متغيرات جديدة. ومع bindings = TRUE فإنه يقفل أيضًا كل متغير موجود، مما يجعل الكائن بأكمله للقراءة فقط:
cfg <- new.env()
cfg$theme <- "dark"
lockEnvironment(cfg, bindings = TRUE)
for (step in c("change", "add")) {
msg <- tryCatch({
if (step == "change") cfg$theme <- "light" else cfg$size <- 12
"done"
}, error = function(e) conditionMessage(e))
cat(step, ":", msg, "\n")
}الناتج:
change : cannot change value of locked binding for 'theme'
add : cannot add bindings to a locked environment local() يشغّل التعليمات البرمجية في بيئة جديدة ويعيد آخر قيمة لها. تبقى الدوال المساعدة المعرّفة بداخلها مخفية، وهذا ما يتيح للملف إبقاء دالة ما خاصة، رغم أن source() يضع كل شيء آخر في البيئة العامة:
circle_area <- local({
square <- function(x) x * x
function(r) pi * square(r)
})
print(round(circle_area(2), 2))
print(exists("square"))الناتج:
[1] 12.57
[1] FALSEالتحدي
سهلأكمل المصنع make_account(owner) في Account.R. يبدأ الرصيد عند 0، ويسجّل سجل خاص كل تغيير على صورة +50 أو -20. تحتوي القائمة المُعادة على أربع دوال:
- تتوقف
deposit(x)معamount must be positiveما لم تكن قيمةxأكبر من 0؛ - تتوقف
withdraw(x)معamount must be positiveأوinsufficient funds؛ - تعيد
balance()الرصيد؛ - تعيد
history()السجل على هيئة متجه محارف.
تحقّق قبل تغيير أي شيء، واستخدم call. = FALSE. يقرأ الكود المُعطى المالك، ثم يقرأ أسطرًا من النوع dep 50 أو wd 20، ويطبع ok أو error: والرسالة لكل منها، ويحاول تغيير نسخة من السجل، ثم يطبع الرصيد والسجل.
ضع كودك في Account.R. يحتوي main.R على كود الإدخال/الإخراج المُعطى، ولا يمكن تعديله.
جرّب بنفسك
source("Account.R")
# كود الإدخال/الإخراج المُزوَّد: أبقه كما هو
input <- suppressWarnings(readLines(file("stdin")))
acc <- make_account(input[1])
for (line in input[-1]) {
p <- strsplit(line, " ")[[1]]
amount <- as.numeric(p[2])
msg <- tryCatch({
if (p[1] == "dep") acc$deposit(amount) else acc$withdraw(amount)
"ok"
}, error = function(e) paste("error:", conditionMessage(e)))
cat(line, ": ", msg, "\n", sep = "")
}
h <- acc$history()
h[1] <- "tampered"
cat("balance: ", acc$balance(), "\n", sep = "")
cat("history: ", paste(acc$history(), collapse = ", "), "\n", sep = "")
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات S3
التعامل مع الملفاتالقوائم باستخدام فئةدوال المُنشئأساليب الطباعة والتنسيقأدوات التحقق والمساعدةمراجعة - درجات الحرارة2S3 الدوال العامة والطرق
الدوال العامة وUseMethodالطرق الافتراضيةطرق الدوال العامة الأساسيةأساسيات NextMethodمراجعة - مساحات الأشكالتدرّب بنفسك: مترجم R عبر الإنترنت