Herança e callSuper
Parte da seção Programação Orientada a Objetos do Journey de R da Coddy. Lição 40 de 57.
contains = "Account" faz uma classe de referência herdar todos os campos e métodos de Account. A subclasse adiciona seus próprios campos e métodos:
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"))Saída:
210 TRUEUma subclasse sobrescreve um método definindo outro com o mesmo nome. Dentro dele, callSuper(...) chama a versão da classe pai, portanto a classe filha adiciona apenas o que é novo:
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")Saída:
limit is 500
insufficient funds
100 initialize é herdado e sobrescrito como qualquer método. O initialize de uma classe filha chama callSuper(...) para permitir que a classe pai defina seus campos e, em seguida, define seus próprios valores padrão:
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)Saída:
Bo 0 0.01Objetos de diferentes subclasses respondem à mesma chamada de método à sua própria maneira, portanto um loop sobre uma lista mista não precisa verificar a 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)))Saída:
98 101Desafio
FácilContas e contas-poupança são Reference Classes em dois arquivos. Complete os métodos:
AccountemAccount.R(camposowner,balance):initializedefine o saldo como 0 quando ele não é fornecido;deposit(x);withdraw(x)interrompe cominsufficient funds;month_end()cobra uma taxa de 1;describe()retornaada: 99.SavingsemSavings.R(contémAccount, camporate):withdraw(x)interrompe comlimit is 500acima de 500 e, caso contrário, chamacallSuper(x);month_end()adiciona juros,balance * rate, em vez da taxa;describe()adiciona(savings)comcallSuper().
Use call. = FALSE. Ambas as definições são envolvidas em suppressMessages(). O código fornecido lê savings ada 0.1 ou account bo, depois dep 100, wd 30 ou month, e imprime describe() ou refused: e a mensagem após cada operação.
Seu código deve estar em Account.R e Savings.R. main.R contém o código fornecido de entrada/saída e não pode ser editado.
Experimente você mesmo
source("Account.R")
source("Savings.R")
# Código de entrada/saída fornecido: mantenha-o como está
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 = "")
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos do S3
Trabalhando com ArquivosListas com uma ClasseFunções ConstrutorasMétodos de Impressão e FormataçãoValidadores e AuxiliaresRevisão - Temperaturas4Encapsulamento em R
Closures como ObjetosAmbientes como ObjetosFunções de AcessoProtegendo o EstadoRecapitulação - Parquímetro2Genéricos e Métodos S3
Genéricos e UseMethodMétodos padrãoMétodos para genéricos baseFundamentos de NextMethodRevisão - Áreas de formas5Classes S4
Definindo classes S4Slots e acessoVerificações de validadeMétodos showRevisão - Catálogo de produtos8Classes de Referência
Definindo Classes de ReferênciaMétodos e Atualizações de CamposSemântica de CópiaHerança e callSuperRecapitulação — Fila de Tarefas11Projeto: Gerenciamento de Biblioteca
Livros e MembrosEmpréstimo de LivrosPratique por conta própria: Compilador de R online