Menu
CoddyTech

Kế thừa và callSuper

Thuộc phần Lập trình hướng đối tượng trong hành trình R của Coddy. Bài học 40 trên 57.

contains = "Account" giúp lớp tham chiếu kế thừa mọi trường và phương thức của Account. Lớp con thêm các trường và phương thức riêng:

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

Đầu ra:

210 TRUE

Lớp con ghi đè một phương thức bằng cách định nghĩa phương thức cùng tên. Bên trong phương thức đó, callSuper(...) gọi phiên bản của lớp cha, để lớp con chỉ thêm những gì mới:

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

Đầu ra:

limit is 500 
insufficient funds 
100 

initialize được kế thừa và ghi đè như mọi phương thức. initialize của lớp con gọi callSuper(...) để lớp cha thiết lập các trường của nó, rồi đặt các giá trị mặc định riêng:

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)

Đầu ra:

Bo 0 0.01

Các đối tượng thuộc những lớp con khác nhau sẽ xử lý cùng một lời gọi phương thức theo cách riêng, vì vậy vòng lặp qua danh sách gồm nhiều kiểu đối tượng không cần kiểm tra lớp:

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

Đầu ra:

98 101
challenge icon

Thử thách

Dễ

Tài khoản và tài khoản tiết kiệm là các lớp tham chiếu trong hai tệp. Hoàn thiện các phương thức:

  • Account trong Account.R (các trường owner, balance): initialize đặt số dư thành 0 nếu không được cung cấp; deposit(x); withdraw(x) dừng với insufficient funds; month_end() thu phí 1; describe() trả về ada: 99.
  • Savings trong Savings.R (chứa Account, trường rate): withdraw(x) dừng với limit is 500 nếu vượt quá 500, còn không thì gọi callSuper(x); month_end() cộng tiền lãi, balance * rate, thay vì thu phí; describe() thêm (savings) bằng callSuper().

Dùng call. = FALSE. Cả hai định nghĩa đều được bọc trong suppressMessages(). Code được cung cấp đọc savings ada 0.1 hoặc account bo, sau đó đọc dep 100, wd 30 hoặc month, rồi in ra describe() hoặc refused: cùng thông báo sau mỗi thao tác.

Code của bạn đặt trong Account.R và Savings.R. main.R chứa code đầu vào/đầu ra được cung cấp và không thể chỉnh sửa.

Tự làm thử

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

# Đoạn code đầu vào/đầu ra có sẵn: giữ nguyên
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 iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch R online