Héritage et callSuper
Fait partie de la section Programmation orientée objet du Journey R de Coddy. Leçon 40 sur 57.
contains = "Account" fait hériter à une classe de référence tous les champs et méthodes de Account. La sous-classe ajoute ses propres champs et méthodes :
Account <- suppressMessages(setRefClass("Account",
fields = list(owner = "character", balance = "numeric"),
methods = list(deposit = function(x) balance <<- balance + x)
))
Savings <- suppressMessages(setRefClass("Savings", contains = "Account",
fields = list(rate = "numeric"),
methods = list(add_interest = function() deposit(balance * rate))
))
s <- Savings$new(owner = "Ada", balance = 100, rate = 0.05)
s$deposit(100)
s$add_interest()
cat(s$balance, is(s, "Account"))Sortie :
210 TRUEUne sous-classe redéfinit une méthode en en définissant une portant le même nom. À l’intérieur, callSuper(...) appelle la version de la classe parente, de sorte que la classe enfant n’ajoute que ce qui est nouveau :
Account <- suppressMessages(setRefClass("Account",
fields = list(balance = "numeric"),
methods = list(withdraw = function(x) {
if (x > balance) stop("insufficient funds", call. = FALSE)
balance <<- balance - x
})
))
Savings <- suppressMessages(setRefClass("Savings", contains = "Account",
methods = list(withdraw = function(x) {
if (x > 500) stop("limit is 500", call. = FALSE)
callSuper(x)
})
))
s <- Savings$new(balance = 300)
s$withdraw(200)
for (x in c(600, 400)) cat(tryCatch({ s$withdraw(x); "ok" }, error = function(e) conditionMessage(e)), "\n")
cat(s$balance, "\n")Sortie :
limit is 500
insufficient funds
100 initialize est hérité et redéfini comme toute méthode. Le initialize de l'enfant appelle callSuper(...) pour laisser le parent définir ses champs, puis définit ses propres valeurs par défaut :
Account <- suppressMessages(setRefClass("Account",
fields = list(owner = "character", balance = "numeric"),
methods = list(initialize = function(...) {
initFields(...)
if (length(balance) == 0) balance <<- 0
invisible(.self)
})
))
Savings <- suppressMessages(setRefClass("Savings", contains = "Account",
fields = list(rate = "numeric"),
methods = list(initialize = function(...) {
callSuper(...)
if (length(rate) == 0) rate <<- 0.01
invisible(.self)
})
))
s <- Savings$new(owner = "Bo")
cat(s$owner, s$balance, s$rate)Sortie :
Bo 0 0.01Les objets de différentes sous-classes répondent à un même appel de méthode à leur manière ; une boucle parcourant une liste mixte n’a donc besoin d’aucune vérification de la classe :
Account <- suppressMessages(setRefClass("Account",
fields = list(balance = "numeric"),
methods = list(month_end = function() balance <<- balance - 2)
))
Savings <- suppressMessages(setRefClass("Savings", contains = "Account",
methods = list(month_end = function() balance <<- balance * 1.01)
))
bank <- list(Account$new(balance = 100), Savings$new(balance = 100))
for (a in bank) a$month_end()
cat(vapply(bank, function(a) a$balance, numeric(1)))Sortie :
98 101Défi
FacileLes comptes et les comptes d’épargne sont des classes de référence réparties dans deux fichiers. Complétez les méthodes :
AccountdansAccount.R(champsowner,balance) :initializedéfinit le solde à 0 lorsqu’il n’est pas fourni ;deposit(x);withdraw(x)s’arrête avecinsufficient funds;month_end()prélève des frais de 1 ;describe()renvoieada: 99.SavingsdansSavings.R(contientAccount, champrate) :withdraw(x)s’arrête aveclimit is 500au-dessus de 500 et appelle sinoncallSuper(x);month_end()ajoute les intérêts,balance * rate, au lieu des frais ;describe()ajoute(savings)aveccallSuper().
Utilisez call. = FALSE. Les deux définitions sont enveloppées dans suppressMessages(). Le code fourni lit savings ada 0.1 ou account bo, puis dep 100, wd 30 ou month, et affiche describe() ou refused: ainsi que le message après chaque opération.
Votre code doit être placé dans Account.R et Savings.R. main.R contient le code fourni d’entrée/sortie et ne peut pas être modifié.
Essayez vous-même
source("Account.R")
source("Savings.R")
# Code d'entrée/sortie fourni : le garder tel quel
input <- suppressWarnings(readLines(file("stdin")))
p <- strsplit(input[1], " ")[[1]]
acc <- if (p[1] == "savings") Savings$new(owner = p[2], rate = as.numeric(p[3])) else Account$new(owner = p[2])
for (line in input[-1]) {
q <- strsplit(line, " ")[[1]]
msg <- tryCatch({
if (q[1] == "dep") acc$deposit(as.numeric(q[2]))
if (q[1] == "wd") acc$withdraw(as.numeric(q[2]))
if (q[1] == "month") acc$month_end()
acc$describe()
}, error = function(e) paste("refused:", conditionMessage(e)))
cat(line, ": ", msg, "\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