S3 y S4 parecen ser los enfoques oficiales (es decir, integrados) para la programación OO. Comencé a usar una combinación de S3 con funciones integradas en la función / método del constructor. Mi objetivo era tener una sintaxis de tipo object $ method () para tener campos semiprivados. Digo semiprivado porque no hay forma de ocultarlos realmente (hasta donde yo sé). Aquí hay un ejemplo simple que en realidad no hace nada:
EmailClass <- function(name, email) {
nc = list(
name = name,
email = email,
get = function(x) nc[[x]],
set = function(x, value) nc[[x]] <<- value,
props = list(),
history = list(),
getHistory = function() return(nc$history),
getNumMessagesSent = function() return(length(nc$history))
)
nc$sendMail = function(to) {
cat(paste("Sending mail to", to, 'from', nc$email))
h <- nc$history
h[[(length(h)+1)]] <- list(to=to, timestamp=Sys.time())
assign('history', h, envir=nc)
}
nc$addProp = function(name, value) {
p <- nc$props
p[[name]] <- value
assign('props', p, envir=nc)
}
nc <- list2env(nc)
class(nc) <- "EmailClass"
return(nc)
}
print.EmailClass <- function(x) {
if(class(x) != "EmailClass") stop();
cat(paste(x$get("name"), "'s email address is ", x$get("email"), sep=''))
}
Y un código de prueba:
test <- EmailClass(name="Jason", "jason@bryer.org")
test$addProp('hello', 'world')
test$props
test
class(test)
str(test)
test$get("name")
test$get("email")
test$set("name", "Heather")
test$get("name")
test
test$sendMail("jbryer@excelsior.edu")
test$getHistory()
test$sendMail("test@domain.edu")
test$getNumMessagesSent()
test2 <- EmailClass("Nobody", "dontemailme@nowhere.com")
test2
test2$props
test2$getHistory()
test2$sendMail('nobody@exclesior.edu')
Aquí hay un enlace a una publicación de blog que escribí sobre este enfoque: http://bryer.org/2012/object-oriented-programming-in-r Agradecería comentarios, críticas y sugerencias sobre este enfoque, ya que no estoy convencido. yo mismo si este es el mejor enfoque. Sin embargo, para el problema que estaba tratando de resolver, funcionó muy bien. Específicamente, para el paquete makeR ( http://jbryer.github.com/makeR ) no quería que los usuarios cambiaran los campos de datos directamente porque necesitaba asegurarme de que un archivo XML que representaba el estado de mi objeto se mantuviera sincronizado. Esto funcionó perfectamente siempre que los usuarios se adhieran a las reglas que describo en la documentación.