Menu
Coddy logo textTech

setValidity와 validObject

Coddy R 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 57개 중 30번째.

setValidity("Class", function(object) ...)는 클래스가 선언된 후 유효성 규칙을 추가하므로, 긴 setClass() 호출을 짧게 유지할 수 있습니다. setGeneric()과 마찬가지로 최상위 수준에서 출력되는 값을 반환하므로 invisible()로 감싸세요:

setClass("Account", slots = c(owner = "character", balance = "numeric"))
invisible(setValidity("Account", function(object) {
  if (object@balance < 0) return("balance must not be negative")
  TRUE
}))
msg <- tryCatch(new("Account", owner = "Ada", balance = -1), error = function(e) conditionMessage(e))
cat(msg)

출력:

invalid class “Account” object: balance must not be negative

변경은 결과를 확인하는 메서드를 통해 이루어져야 합니다. withdraw() 메서드는 슬롯을 변경하고, validObject()를 호출한 다음, 객체가 여전히 유효할 때만 객체를 반환합니다.

setClass("Account", slots = c(balance = "numeric"))
invisible(setValidity("Account", function(object) {
  if (object@balance < 0) "balance must not be negative" else TRUE
}))
invisible(setGeneric("withdraw", function(acc, amount) standardGeneric("withdraw")))
setMethod("withdraw", "Account", function(acc, amount) {
  acc@balance <- acc@balance - amount
  validObject(acc)
  acc
})
a <- new("Account", balance = 50)
a <- withdraw(a, 20)
msg <- tryCatch({ a <- withdraw(a, 100); "done" }, error = function(e) "refused")
cat(msg, a@balance)

출력:

refused 30

접근자 쌍은 제네릭으로 만들 수 있습니다. balance(x)는 읽고 대체 제네릭 "balance<-"은 씁니다. 해당 메서드는 객체를 검사하므로 balance(a) <- -5와 같은 잘못된 변경은 다른 경우와 마찬가지로 거부됩니다.

setClass("Account", slots = c(balance = "numeric"),
  validity = function(object) if (object@balance < 0) "balance must not be negative" else TRUE)
invisible(setGeneric("balance", function(x) standardGeneric("balance")))
invisible(setGeneric("balance<-", function(x, value) standardGeneric("balance<-")))
setMethod("balance", "Account", function(x) x@balance)
setMethod("balance<-", "Account", function(x, value) {
  x@balance <- value
  validObject(x)
  x
})
a <- new("Account", balance = 10)
balance(a) <- 25
msg <- tryCatch({ balance(a) <- -5; "changed" }, error = function(e) "refused")
cat(balance(a), msg)

출력:

25 refused

validObject(x, test = TRUE)는 중단하지 않습니다. 유효한 객체이면 TRUE를 반환하고, 그렇지 않으면 문제 텍스트를 반환하므로 보고서에 유용합니다:

setClass("Box", slots = c(w = "numeric"),
  validity = function(object) if (object@w <= 0) "w must be positive" else TRUE)
b <- new("Box", w = 2)
print(validObject(b, test = TRUE))
b@w <- 0
print(validObject(b, test = TRUE))

출력:

[1] TRUE
[1] "w must be positive"
challenge icon

챌린지

쉬움

Account.R의 Account 클래스에는 owner와 balance 슬롯이 있습니다. 파일을 완성하세요:

  • setValidity() ( invisible()로 감싼 형태)를 사용하여 owner must not be empty 또는 balance must not be negative를 반환하는 규칙을 추가하세요;
  • deposit(acc, amount)와 withdraw(acc, amount) 제네릭 및 해당 Account 메서드를 만드세요. deposit은 금액이 0 이하일 때 amount must be positive와 함께 중단해야 합니다(call. = FALSE 사용). 두 메서드 모두 잔액을 변경하고, validObject()를 호출한 다음 계정을 반환해야 합니다;
  • ada: 50을 출력하고 줄바꿈하는 show() 메서드를 추가하세요.

제공된 코드는 소유자를 읽은 다음 dep 50 또는 wd 20 형식의 줄을 읽고, 변경할 때마다 계정을 출력하거나 refused: 와 문제 내용을 출력합니다.

코드는 Account.R에 작성하세요. main.R에는 제공된 입력/출력 코드가 들어 있으며 편집할 수 없습니다.

직접 해보기

source("Account.R")

# 제공된 입출력 코드: 그대로 유지하세요
input <- suppressWarnings(readLines(file("stdin")))
acc <- tryCatch(new("Account", owner = input[1], balance = 0),
                error = function(e) { cat("error: ", sub(".*object: ", "", conditionMessage(e)), "\n", sep = ""); NULL })
if (!is.null(acc)) {
  for (line in input[-1]) {
    p <- strsplit(line, " ")[[1]]
    amount <- as.numeric(p[2])
    acc <- tryCatch({
      changed <- if (p[1] == "dep") deposit(acc, amount) else withdraw(acc, amount)
      print(changed)
      changed
    }, error = function(e) {
      cat("refused: ", sub(".*object: ", "", conditionMessage(e)), "\n", sep = "")
      acc
    })
  }
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 R 컴파일러