My approach is to create a secured environment (called SEnv
below) where all objects are checked by validObject
before entering into this secured environment. Then, anytime you take out any objects from this secured environment, you can be sure they are valid objects without rechecking them.
setClass("SEnv", contains = "environment")
setMethod(
"[[<-",
signature(x = "SEnv", i = "character", j = "missing", value = "ANY"),
function(x, i, j, ..., value) {
validObject(value, complete = TRUE)
ev <- as(x, "environment")
ev[[i]] <- value
lockBinding(i, ev)
x
}
)
setMethod(
"$<-",
signature(x = "SEnv", value = "ANY"),
function(x, name, value) {
validObject(value, complete = TRUE)
ev <- as(x, "environment")
ev[[name]] <- value
lockBinding(name, ev)
x
}
)
Example
- Let's define a
C1
class with 2 slots: x
as integer and y
as character. We also add a constraint that x
and y
must have the same length.
setClass("C1", slots = c(x = "integer", y = "character"))
setValidity("C1", function(object) {
res <- rep(NA_character_, 1)
if (length(object@x) != length(object@y)) res[[1]] <- "length of slot x and y must equal"
res <- c(na.omit(res))
res <- if (length(res)) res else TRUE
res
})
- The
C2
class extends the C1
class and adds an additional constraint which requires slot x
to be sorted.
setClass("C2", contains = c("C1"))
setValidity("C2", function(object) {
res <- rep(NA_character_, 1)
if (is.unsorted(object@x) && is.unsorted(rev(object@x))) res[[1]] <- "slot x is not sorted"
res <- c(na.omit(res))
res <- if (length(res)) res else TRUE
res
})
- Test drive
ee <- new("SEnv")
ee[["o1"]] <- new("C2", x = c(10L:1L), y = rep("A", 10))
ee$o2 <- 1:5 # this is a valid integer object
ee$o2 <- 6:10 # object `o2` is locked and cannot change
# Try to create a malformed `C2` object
o_bad <- ee$o1
o_bad@x <- c(5L, 6L, 7L)
o_bad # Even though the object is a malformed `C2`, it is still printed as "An object of class "C2"" and method dispatch through signature `C2`.
#An object of class "C2"
#Slot "x":
#[1] 5 6 7
#Slot "y":
# [1] "A" "A" "A" "A" "A" "A" "A" "A" "A" "A"
ee$o3 <- o_bad # this bad object cannot enter our secured environment due to failure of validity method of its superclass `C1`
# Error in validObject(value, complete = TRUE) :
# invalid class “C2” object: length of slot x and y must equal