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 TRUEUna 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.01Gli 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 101Sfida
FacileI conti e i conti di risparmio sono Reference Classes in due file. Completa i metodi:
AccountinAccount.R(campiowner,balance):initializeimposta il saldo a 0 quando non è specificato;deposit(x);withdraw(x)si interrompe coninsufficient funds;month_end()addebita una commissione di 1;describe()restituisceada: 99.SavingsinSavings.R(contieneAccount, camporate):withdraw(x)si interrompe conlimit is 500se supera 500, altrimenti chiamacallSuper(x);month_end()aggiunge gli interessi,balance * rate, invece della commissione;describe()aggiunge(savings)concallSuper().
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 = "")
}
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
1Nozioni di base su S3
Lavorare con i fileListe con una classeFunzioni costruttriciMetodi di stampa e formattazioneValidatori e funzioni di supportoRipasso - Temperature4Incapsulamento in R
Chiusure come oggettiAmbienti come oggettiFunzioni di accessoProteggere lo statoRiepilogo - Parcometro2Generics e metodi S3
Generics e UseMethodMetodi predefinitiMetodi per i generics di baseNozioni di base su NextMethodRiepilogo - Aree delle forme5Classi S4
Definire le classi S4Slot e accessoControlli di validitàMetodi showRiepilogo - Catalogo prodotti8Classi di riferimento
Definizione delle classi di riferimentoMetodi e aggiornamenti dei campiSemantica della copiaEreditarietà e callSuperRiepilogo - coda di attività11Progetto: Gestione della biblioteca
Libri e iscrittiPrestito dei libriEsercitati da solo: Compilatore R online