Menu
Coddy logo textTech

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 TRUE

Une 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.01

Les 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 101
challenge icon

Défi

Facile

Les comptes et les comptes d’épargne sont des classes de référence réparties dans deux fichiers. Complétez les méthodes :

  • Account dans Account.R (champs owner, balance) : initialize définit le solde à 0 lorsqu’il n’est pas fourni ; deposit(x) ; withdraw(x) s’arrête avec insufficient funds ; month_end() prélève des frais de 1 ; describe() renvoie ada: 99.
  • Savings dans Savings.R (contient Account, champ rate) : withdraw(x) s’arrête avec limit is 500 au-dessus de 500 et appelle sinon callSuper(x) ; month_end() ajoute les intérêts, balance * rate, au lieu des frais ; describe() ajoute (savings) avec callSuper().

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 = "")
}
quiz iconTestez-vous

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

Entraînez-vous par vous-même : Compilateur R en ligne