setValidity e validObject
Parte da seção Programação Orientada a Objetos do Journey de R da Coddy. Lição 30 de 57.
setValidity("Class", function(object) ...) adiciona a regra de validade depois que a classe é declarada, o que mantém curta uma chamada longa de setClass(). Assim como setGeneric(), ela retorna um valor que é impresso no nível superior; portanto, envolva-a em 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)Saída:
invalid class “Account” object: balance must not be negativeAs alterações devem passar por métodos que verificam o resultado. Um método withdraw() altera o slot, chama validObject() e retorna o objeto somente quando ele ainda é válido:
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)Saída:
refused 30Um par de acessores pode ser composto por genéricos: balance(x) faz a leitura, e um genérico de substituição "balance<-" faz a escrita. Seu método verifica o objeto, portanto balance(a) <- -5 é recusado como qualquer outra alteração inválida:
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)Saída:
25 refusedvalidObject(x, test = TRUE) não interrompe a execução. Ele retorna TRUE para um objeto válido ou o texto do problema, o que é útil para um relatório:
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))Saída:
[1] TRUE
[1] "w must be positive"Desafio
FácilA classe Account em Account.R tem os slots owner e balance. Complete o arquivo:
- adicione uma regra com
setValidity()(envolvida eminvisible()) que retorneowner must not be emptyoubalance must not be negative; - crie os genéricos
deposit(acc, amount)ewithdraw(acc, amount)e seus métodos deAccount:depositdeve interromper comamount must be positive(usecall. = FALSE) para valores de 0 ou menos; ambos devem alterar o saldo, chamarvalidObject()e retornar a conta; - adicione um método
show()que imprimaada: 50e uma nova linha.
O código fornecido lê o proprietário, depois linhas dep 50 ou wd 20, e imprime a conta após cada alteração ou refused: e o problema.
Seu código deve ser colocado em Account.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")
# Código de entrada/saída fornecido: mantenha-o como está
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
})
}
}
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 Livros3Herança S3
Vetores de classeOrdem de busca de métodosEstendendo com NextMethodVerificando com inherits()Revisão - Folha de pagamento da equipe6S4 Genéricos e Métodos
Criando GenéricosMétodos para Objetos IntegradosDespacho em Dois ArgumentossetValidity e validObjectRevisão - Dinheiro e MoedaPratique por conta própria: Compilador de R online