Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

setValidity و validObject

جزء من قسم البرمجة كائنية التوجه في رحلة R على Coddy. الدرس 30 من 57.

تضيف setValidity("Class", function(object) ...) قاعدة التحقق من الصحة بعد التصريح عن الصنف، مما يُبقي استدعاء setClass() الطويل قصيرًا. وكما هو الحال مع setGeneric()، تُرجع قيمة تُطبع في المستوى الأعلى، لذا لفّها داخل invisible():

setClass("Account", slots = c(owner = "character", balance = "numeric"))
invisible(setValidity("Account", function(object) {
  if (object@balance < 0) return("balance must not be negative")
  TRUE
}))
msg <- tryCatch(new("Account", owner = "Ada", balance = -1), error = function(e) conditionMessage(e))
cat(msg)

المخرجات:

invalid class “Account” object: balance must not be negative

يجب أن تمر التغييرات عبر أساليب تتحقق من النتيجة. يغيّر الأسلوب withdraw() الخانة، ويستدعي validObject()، ويعيد الكائن فقط عندما يظل صالحًا:

setClass("Account", slots = c(balance = "numeric"))
invisible(setValidity("Account", function(object) {
  if (object@balance < 0) "balance must not be negative" else TRUE
}))
invisible(setGeneric("withdraw", function(acc, amount) standardGeneric("withdraw")))
setMethod("withdraw", "Account", function(acc, amount) {
  acc@balance <- acc@balance - amount
  validObject(acc)
  acc
})
a <- new("Account", balance = 50)
a <- withdraw(a, 20)
msg <- tryCatch({ a <- withdraw(a, 100); "done" }, error = function(e) "refused")
cat(msg, a@balance)

الناتج:

refused 30

يمكن أن يتكوّن زوج من دوال الوصول من دوال عامة: تقرأ balance(x)، وتكتب الدالة العامة البديلة "balance<-". تتحقق طريقتها من الكائن، لذا يُرفَض balance(a) <- -5 مثل أي تغيير غير صالح آخر:

setClass("Account", slots = c(balance = "numeric"),
  validity = function(object) if (object@balance < 0) "balance must not be negative" else TRUE)
invisible(setGeneric("balance", function(x) standardGeneric("balance")))
invisible(setGeneric("balance<-", function(x, value) standardGeneric("balance<-")))
setMethod("balance", "Account", function(x) x@balance)
setMethod("balance<-", "Account", function(x, value) {
  x@balance <- value
  validObject(x)
  x
})
a <- new("Account", balance = 10)
balance(a) <- 25
msg <- tryCatch({ balance(a) <- -5; "changed" }, error = function(e) "refused")
cat(balance(a), msg)

الناتج:

25 refused

validObject(x, test = TRUE) لا يتوقف. فهو يعيد TRUE للكائن الصالح، أو نص المشكلة، وهذا مفيد لإعداد تقرير:

setClass("Box", slots = c(w = "numeric"),
  validity = function(object) if (object@w <= 0) "w must be positive" else TRUE)
b <- new("Box", w = 2)
print(validObject(b, test = TRUE))
b@w <- 0
print(validObject(b, test = TRUE))

الناتج:

[1] TRUE
[1] "w must be positive"
challenge icon

التحدي

سهل

يحتوي الصنف Account في Account.R على الفتحتين owner وbalance. أكمل الملف:

  • أضف قاعدة باستخدام setValidity() (مغلّفة داخل invisible()) تُرجع owner must not be empty أو balance must not be negative؛
  • أنشئ الدالتين العامتين deposit(acc, amount) وwithdraw(acc, amount) وطريقتيهما للصنف Account: تتوقف deposit مع amount must be positive (باستخدام call. = FALSE) للمبالغ التي تساوي 0 أو أقل؛ وتغيّر كلتاهما الرصيد، وتستدعي validObject()، وتُرجعان الحساب؛
  • أضف طريقة show() تطبع ada: 50 وسطرًا جديدًا.

يقرأ الكود المُزوَّد اسم المالك، ثم أسطرًا من الشكل dep 50 أو wd 20، ويطبع الحساب بعد كل تغيير أو refused: والمشكلة.

ضع الكود في Account.R. يحتوي main.R على كود الإدخال/الإخراج المُزوَّد ولا يمكن تعديله.

جرّب بنفسك

source("Account.R")

# كود الإدخال/الإخراج المُزوَّد: أبقِه كما هو
input <- suppressWarnings(readLines(file("stdin")))
acc <- tryCatch(new("Account", owner = input[1], balance = 0),
                error = function(e) { cat("error: ", sub(".*object: ", "", conditionMessage(e)), "\n", sep = ""); NULL })
if (!is.null(acc)) {
  for (line in input[-1]) {
    p <- strsplit(line, " ")[[1]]
    amount <- as.numeric(p[2])
    acc <- tryCatch({
      changed <- if (p[1] == "dep") deposit(acc, amount) else withdraw(acc, amount)
      print(changed)
      changed
    }, error = function(e) {
      cat("refused: ", sub(".*object: ", "", conditionMessage(e)), "\n", sep = "")
      acc
    })
  }
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم R عبر الإنترنت