setValidity et validObject
Fait partie de la section Programmation orientée objet du Journey R de Coddy. Leçon 30 sur 57.
setValidity("Class", function(object) ...) ajoute la règle de validité après la déclaration de la classe, ce qui permet de garder un appel setClass() long et concis. Comme setGeneric(), il renvoie une valeur qui est affichée au niveau supérieur ; enveloppez-le donc dans 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)Sortie :
invalid class “Account” object: balance must not be negativeLes modifications doivent passer par des méthodes qui vérifient le résultat. Une méthode withdraw() modifie le slot, appelle validObject() et ne renvoie l’objet que lorsqu’il est toujours valide :
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)Sortie :
refused 30Une paire d’accesseurs peut être constituée de génériques : balance(x) lit et un générique de remplacement "balance<-" écrit. Sa méthode vérifie l’objet, donc balance(a) <- -5 est refusé comme toute autre modification incorrecte :
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)Résultat :
25 refusedvalidObject(x, test = TRUE) ne s’arrête pas. Elle renvoie TRUE pour un objet valide, ou le texte du problème, ce qui est pratique pour un rapport :
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))Sortie :
[1] TRUE
[1] "w must be positive"Défi
FacileLa classe Account dans Account.R possède les emplacements owner et balance. Complétez le fichier :
- ajoutez une règle avec
setValidity()(encapsulée dansinvisible()) qui renvoieowner must not be emptyoubalance must not be negative; - créez les génériques
deposit(acc, amount)etwithdraw(acc, amount)ainsi que leurs méthodesAccount:deposits'arrête avecamount must be positive(utilisezcall. = FALSE) pour les montants inférieurs ou égaux à 0 ; les deux modifient le solde, appellentvalidObject()et renvoient le compte ; - ajoutez une méthode
show()qui afficheada: 50et une nouvelle ligne.
Le code fourni lit le propriétaire, puis les lignes dep 50 ou wd 20, et affiche le compte après chaque modification ou refused: suivi du problème.
Votre code doit être placé dans Account.R. main.R contient le code d'entrée/sortie fourni et ne peut pas être modifié.
Essayez vous-même
source("Account.R")
# Code d'entrée/sortie fourni : le garder tel quel
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
})
}
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation orientée objet
1Bases de S3
Travailler avec les fichiersListes avec une classeFonctions constructricesMéthodes print et formatValidateurs et fonctions utilitairesRécapitulatif - Températures4Encapsulation dans R
Les fermetures comme objetsLes environnements comme objetsFonctions d’accèsProtéger l’étatRécapitulatif - Parcmètre2S3 : génériques et méthodes
Génériques et UseMethodMéthodes par défautMéthodes pour les génériques de baseNotions de base sur NextMethodRécapitulatif - Aires des formes5Classes S4
Définir des classes S4Slots et accèsVérifications de validitéMéthodes showRécapitulatif - Catalogue de produits8Classes de référence
Définition des classes de référenceMéthodes et mises à jour des champsSémantique de copieHéritage et callSuperRécapitulatif - File d’attente des tâches11Projet : gestion de bibliothèque
Livres et membresEmprunter des livres3Héritage S3
Vecteurs de classesOrdre de recherche des méthodesÉtendre avec NextMethodVérifier avec inherits()Récapitulatif - Paie du personnel6S4 Génériques et méthodes
Création de génériquesMéthodes pour les fonctions intégréesDispatch sur deux argumentssetValidity et validObjectRécapitulatif - Argent et deviseEntraînez-vous par vous-même : Compilateur R en ligne