setValidity and validObject
Part of the Object Oriented Programming section of Coddy's R journey. Lesson 30 of 57.
setValidity("Class", function(object) ...) adds the validity rule after the class is declared, which keeps a long setClass() call short. Like setGeneric(), it returns a value that is printed at the top level, so wrap it in 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)Output:
invalid class “Account” object: balance must not be negativeChanges should go through methods that check the result. A withdraw() method changes the slot, calls validObject(), and returns the object only when it is still valid:
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)Output:
refused 30An accessor pair can be made of generics: balance(x) reads and a replacement generic "balance<-" writes. Its method checks the object, so balance(a) <- -5 is refused like any other bad change:
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)Output:
25 refusedvalidObject(x, test = TRUE) does not stop. It returns TRUE for a valid object, or the problem text, which is handy for a report:
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))Output:
[1] TRUE
[1] "w must be positive"Challenge
EasyThe class Account in Account.R has the slots owner and balance. Complete the file:
- add a rule with
setValidity()(wrapped ininvisible()) that returnsowner must not be emptyorbalance must not be negative; - create the generics
deposit(acc, amount)andwithdraw(acc, amount)and theirAccountmethods:depositstops withamount must be positive(usecall. = FALSE) for amounts of 0 or less; both change the balance, callvalidObject()and return the account; - add a
show()method that printsada: 50and a new line.
The supplied code reads the owner, then lines dep 50 or wd 20, and prints the account after each change or refused: and the problem.
Your code goes in Account.R. main.R holds the supplied input/output code and cannot be edited.
Try it yourself
source("Account.R")
# Supplied input/output code: keep it as it is
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
})
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1S3 Basics
Working With FilesLists With a ClassConstructor FunctionsPrint and Format MethodsValidators and HelpersRecap - Temperatures4Encapsulation in R
Closures as ObjectsEnvironments as ObjectsAccessor FunctionsGuarding StateRecap - Parking Meter2S3 Generics and Methods
Generics and UseMethodDefault MethodsMethods for Base GenericsNextMethod BasicsRecap - Shape Areas8Reference Classes
Defining Reference ClassesMethods and Field UpdatesCopy SemanticsInheritance and callSuperRecap - Task Queue11Project: Library Management
Books and MembersBorrowing Books3S3 Inheritance
Class VectorsMethod Lookup OrderExtending With NextMethodChecking With inherits()Recap - Staff Payroll6S4 Generics and Methods
Creating GenericsMethods for Built-insDispatch on Two ArgumentssetValidity and validObjectRecap - Money and CurrencyPractice on your own: Online R compiler