Menu
Coddy logo textTech

Ereditarietà e callSuper

Fa parte della sezione Programmazione orientata agli oggetti del percorso R di Coddy. Lezione 40 di 57.

contains = "Account" fa sì che una Reference Class erediti tutti i campi e i metodi di Account. La sottoclasse aggiunge i propri campi e metodi:

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"))

Output:

210 TRUE

Una sottoclasse sovrascrive un metodo definendone uno con lo stesso nome. Al suo interno, callSuper(...) richiama la versione della classe genitore, così la classe figlia aggiunge solo ciò che è nuovo:

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")

Output:

limit is 500 
insufficient funds 
100 

initialize viene ereditato e ridefinito come qualsiasi metodo. Il metodo initialize di una classe figlia chiama callSuper(...) per consentire alla classe genitore di impostare i propri campi, poi imposta i valori predefiniti della classe figlia:

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)

Risultato:

Bo 0 0.01

Gli oggetti di sottoclassi diverse rispondono alla stessa chiamata di metodo a modo loro, quindi un ciclo su un elenco misto non richiede controlli della 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)))

Output:

98 101
challenge icon

Sfida

Facile

I conti e i conti di risparmio sono Reference Classes in due file. Completa i metodi:

  • Account in Account.R (campi owner, balance): initialize imposta il saldo a 0 quando non è specificato; deposit(x); withdraw(x) si interrompe con insufficient funds; month_end() addebita una commissione di 1; describe() restituisce ada: 99.
  • Savings in Savings.R (contiene Account, campo rate): withdraw(x) si interrompe con limit is 500 se supera 500, altrimenti chiama callSuper(x); month_end() aggiunge gli interessi, balance * rate, invece della commissione; describe() aggiunge (savings) con callSuper().

Usa call. = FALSE. Entrambe le definizioni sono racchiuse in suppressMessages(). Il codice fornito legge savings ada 0.1 o account bo, poi dep 100, wd 30 o month, e stampa describe() oppure refused: e il messaggio dopo ciascuna operazione.

Il tuo codice va inserito in Account.R e Savings.R. main.R contiene il codice di input/output fornito e non può essere modificato.

Provalo tu

source("Account.R")
source("Savings.R")

# Supplied input/output code: keep it as it is
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 iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore R online