Dziedziczenie i callSuper
Część sekcji Programowanie obiektowe ścieżki R w Coddy. Lekcja 40 z 57.
contains = "Account" sprawia, że klasa referencyjna dziedziczy wszystkie pola i metody klasy Account. Podklasa dodaje własne pola i metody:
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"))Wynik:
210 TRUEPodklasa nadpisuje metodę, definiując metodę o tej samej nazwie. Wewnątrz niej callSuper(...) wywołuje wersję rodzica, więc klasa potomna dodaje tylko to, co nowe:
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")Wynik:
limit is 500
insufficient funds
100 initialize jest dziedziczona i nadpisywana tak jak każda metoda. initialize obiektu potomnego wywołuje callSuper(...), aby umożliwić klasie nadrzędnej ustawienie jej pól, a następnie ustawia własne wartości domyślne:
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)Wynik:
Bo 0 0.01Obiekty różnych podklas odpowiadają na to samo wywołanie metody na swój sposób, więc pętla przechodząca przez mieszaną listę nie wymaga sprawdzania klasy:
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)))Wynik:
98 101Wyzwanie
ŁatwyKonta i konta oszczędnościowe to klasy referencyjne w dwóch plikach. Uzupełnij metody:
AccountwAccount.R(polaowner,balance):initializeustawia saldo na 0, jeśli nie zostało podane;deposit(x);withdraw(x)kończy działanie komunikateminsufficient funds;month_end()pobiera opłatę w wysokości 1;describe()zwracaada: 99.SavingswSavings.R(zawieraAccount, polerate):withdraw(x)kończy działanie komunikatemlimit is 500, gdy wartość przekracza 500, a w przeciwnym razie wywołujecallSuper(x);month_end()dodaje odsetki,balance * rate, zamiast pobierać opłatę;describe()dodaje(savings)za pomocącallSuper().
Użyj call. = FALSE. Obie definicje są opakowane w suppressMessages(). Dostarczony kod odczytuje savings ada 0.1 lub account bo, następnie dep 100, wd 30 lub month i po każdej operacji wypisuje describe() albo refused: oraz komunikat.
Twój kod umieść w plikach Account.R i Savings.R. main.R zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.
Spróbuj swoich sił
source("Account.R")
source("Savings.R")
# Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
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 = "")
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy S3
Praca z plikamiListy z klasąFunkcje konstruktoraMetody print i formatWalidatory i funkcje pomocniczePowtórzenie — temperatury4Enkapsulacja w R
Domknięcia jako obiektyŚrodowiska jako obiektyFunkcje dostępoweOchrona stanuPodsumowanie – parkometr2Generics i metody S3
Generics i UseMethodMetody domyślneMetody dla podstawowych genericsPodstawy NextMethodPowtórzenie — pola figur5Klasy S4
Definiowanie klas S4Sloty i dostępSprawdzanie poprawnościMetody wyświetlaniaPodsumowanie – katalog produktów8Klasy referencyjne
Definiowanie klas referencyjnychMetody i aktualizacja pólSemantyka kopiowaniaDziedziczenie i callSuperPodsumowanie – kolejka zadań11Projekt: Zarządzanie biblioteką
Książki i czytelnicyWypożyczanie książek3Dziedziczenie S3
Wektory klasKolejność wyszukiwania metodRozszerzanie za pomocą NextMethodSprawdzanie za pomocą inherits()Powtórzenie — lista płac pracownikówPoćwicz samodzielnie: Kompilator R online