Up-front: I am aware that R is a functional language, so please don't bite ;-)
I've had great experiences with using an OOP approach for a lot of my programs. Now, I'm wondering if there's a way to make a distinction between public and private methods when using S4 Reference Classes in R?
Example
Class Definitions
setRefClass("B",
field=list(
b.1="numeric",
b.2="logical"
),
methods=list(
thisIsPublic=function(...) {
thisIsPublic_ref(.self=.self, ...)
},
thisIsPrivate=function(...) {
thisIsPrivate_ref(.self=.self, ...)
}
)
)
setRefClass("A",
field=list(
a.1="B"
)
)
NOTE
I usually do not place the actual method definition within the class def but separate it to a S4 method (i.e. thisIsPublic_ref
) for the following reasons:
- That way the class def stays clearly arranged and is easier to read in cases when the individual method defs grow quite large.
- It allows you to switch to a functional execution of methods at any time. Be
x
an instance of a certain class, you are able to callfoo_ref(.self=x)
instead ofx$foo()
. - It allows you to byte-compile the methods via
compiler::cmpfun()
which I think is not possible if you have "plain" Reference Class methods.
It sure does not really make sense to make it that complicated for this specific example, but I thought I'd nevertheless illustrate that approach.
Method Definitions
setGeneric(
name="thisIsPublic_ref",
signature=c(".self"),
def=function(
.self,
...
) {
standardGeneric("thisIsPublic_ref")
}
)
setGeneric(
name="thisIsPrivate_ref",
signature=c(".self"),
def=function(
.self,
...
) {
standardGeneric("thisIsPrivate_ref")
}
)
require(compiler)
setMethod(
f="thisIsPublic_ref",
signature=signature(.self="B"),
definition=cmpfun(function(
.self,
...
){
.self$b.1 * 1000
})
)
setMethod(
f="thisIsPrivate_ref",
signature=signature(.self="B"),
definition=cmpfun(function(
.self,
...
){
.self$b.2
})
)
Instances
x.b <- new("B", b.1=10, b.2=TRUE)
x.a <- new("A", a.1=x.b, a.2="hello world")
Public vs. private
Instances of class A
(i.e. x.a
) should be allowed to use class B
's public methods:
> x.a$a.1$thisIsPublic()
[1] 10000
Instances of class A
(i.e. x.a
) should not be allowed to use class B
's private methods. So I would want this not to work, i.e. result in an error:
> x.a$a.1$thisIsPrivate()
[1] TRUE
Any idea how one could specify this?
The only thing I came up with so far:
Adding a sender
argument to each method, explicitly specify it for each method call and check if class(.self) == class(sender)
. But that seems a bit “explicit“.