Protéger l’état
Fait partie de la section Programmation orientée objet du Journey R de Coddy. Leçon 20 sur 57.
Un setter est le seul endroit où une valeur peut changer ; c’est donc l’endroit où appliquer les règles. Il vérifie d’abord et appelle stop() avant toute modification, afin que l’objet reste valide :
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())Sortie :
insufficient funds / 50lockBinding("rate", e) rend une variable d’un environnement accessible en lecture seule. Toute affectation ultérieure provoque une erreur, tandis que les autres variables restent modifiables :
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")Sortie :
cannot change value of locked binding for 'rate'
0.05 900 lockEnvironment(e) empêche l’ajout de nouvelles variables. Avec bindings = TRUE, il verrouille également chaque variable existante, ce qui rend l’objet entier accessible en lecture seule :
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")
}Sortie :
change : cannot change value of locked binding for 'theme'
add : cannot add bindings to a locked environment local() exécute du code dans un environnement vierge et renvoie sa dernière valeur. Les fonctions auxiliaires définies à l’intérieur restent masquées, ce qui permet à un fichier de conserver une fonction privée même si source() place tout le reste dans l’environnement global :
circle_area <- local({
square <- function(x) x * x
function(r) pi * square(r)
})
print(round(circle_area(2), 2))
print(exists("square"))Sortie :
[1] 12.57
[1] FALSEDéfi
FacileComplétez la fabrique make_account(owner) dans Account.R. Le solde commence à 0 et un historique privé enregistre chaque modification sous la forme +50 ou -20. La liste renvoyée contient quatre fonctions :
deposit(x)s'arrête avecamount must be positivesauf sixest supérieur à 0 ;withdraw(x)s'arrête avecamount must be positiveouinsufficient funds;balance()renvoie le solde ;history()renvoie l'historique sous forme de vecteur de caractères.
Effectuez les vérifications avant toute modification et utilisez call. = FALSE. Le code fourni lit le propriétaire, puis des lignes dep 50 ou wd 20, affiche ok ou error: ainsi que le message pour chacune, tente de modifier une copie de l'historique, puis affiche le solde et l'historique.
Votre code doit être placé dans Account.R. main.R contient le code fourni d'entrée/sortie 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 <- 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 = "")
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 personnelEntraînez-vous par vous-même : Compilateur R en ligne