My goal is to modify the behavior of the initialize method depending on the type of the argument. For example, if I call MyClass$new(list())
I'd like to initialize my object with a list, and if I call MyClass$new("foo")
I'd like to initialize the object with a character string. I think S3 generics are very useful for that, so I did something like this:
initialize_test_class <- function(x) UseMethod("initialize_test_class")
initialize_test_class.default <- function(x) {
warning("don't know what to do")
}
initialize_test_class.list <- function(x) {
print("initialized with a list")
}
initialize_test_class.character <- function(x) {
print("initialized with a character")
}
TestClass <- R6::R6Class(
classname = "TestClass",
public = list(
initialize = function(x) {
initialize_test_class(x)
}
)
)
TestClass$new(list(a = 1))
TestClass$new("foo")
And it works, but I'm wondering maybe there's a more elegant way to do it? Some more general advices about OOP good practices in such cases are welcome, too.
If initialization of the R6 class is the sole purpose of your S3 class, I would say you are definitely over-engineering this. There are other methods for type-checking input that are simpler (and possibly safer) than relying on S3 dispatch. For example is.list(x)
and is.character(x)
might be useful here, or inherits(x, 'list')
and inherits(x, 'character')
.
So I think a more elegant solution here would be:
TestClass <- R6::R6Class(
classname = "TestClass",
public = list(
initialize = function(x) {
if(is.list(x)) cat('initialized with a list\n') else
if(is.character(x)) cat('initialized with a character\n') else
cat("I don't know what to do\n")
}
)
)
Though depending on the complexity of your initialization methods, you may wish to bring the methods outside of the class definition, but don't have them as S3:
init_list <- function(x) cat('initialized with a list\n')
init_char <- function(x) cat('initialized with a character\n')
init_none <- function(x) cat("I don't know what to do\n")
TestClass <- R6::R6Class(
classname = "TestClass",
public = list(
initialize = function(x) {
if(is.list(x)) init_list(x) else
if(is.character(x)) init_char(x) else init_none(x)
}
)
)
Both of which give the same result:
TestClass$new(list(a = 1))
#> initialized with a list
#> <TestClass>
#> Public:
#> clone: function (deep = FALSE)
#> initialize: function (x)
TestClass$new("foo")
#> initialized with a character
#> <TestClass>
#> Public:
#> clone: function (deep = FALSE)
#> initialize: function (x)
Created on 2023-09-21 with reprex v2.0.2