S4参考类是否有声明公共和私有方法的方法?

25
请注意:我知道R是一种函数式语言,所以请不要咬我;-)
对于我的许多程序,采用面向对象编程方法给我带来了很好的体验。现在,我想知道在R中使用S4 Reference Classes时是否有办法区分公共方法和私有方法?
示例
类定义
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"
    )
)

注意

通常我不会将实际的方法定义放在类定义中,而是将其分离为S4方法(即thisIsPublic_ref),原因如下:

  1. 这样类定义保持清晰排列,在个别方法定义变得很大时更容易阅读。
  2. 它允许您随时切换到方法的功能性执行。假设x是某个类的实例,则可以调用foo_ref(.self=x)而不是x$foo()
  3. 它允许您通过compiler::cmpfun()字节编译方法,我认为如果您只有“纯”引用类方法,则无法实现。

对于这个特定的示例来说,确实没有必要使它那么复杂,但我想仍然说明这种方法。

方法定义

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
    })
)

实例

x.b <- new("B", b.1=10, b.2=TRUE)
x.a <- new("A", a.1=x.b, a.2="hello world")

公有 vs. 私有

A 的实例(即 x.a)应该允许使用类 B公有方法:

> x.a$a.1$thisIsPublic()
[1] 10000

A 的实例 (即 x.a) 不应该允许使用类 B私有方法。因此,我希望这样的操作起作用,即导致错误:

> x.a$a.1$thisIsPrivate()
[1] TRUE

有什么想法可以指定这个吗?

到目前为止,我想到的唯一方法:

在每个方法中添加一个sender参数,显式地为每个方法调用指定它,并检查class(.self) == class(sender)。但这似乎有点“显式”。


x.a是A类的一个实例,但x.a$a.1是B类的一个实例。你想阻止B类的一个实例访问B类的私有方法吗?如果你试图基于它可能存在的数据结构来阻止一个类访问其方法,那么你很可能会进入一个完全痛苦的世界... - Spacedman
完全正确,但这不是我的目标。再次强调,我感觉自己在面向对象编程方面缺乏一些背景知识。将某些类的实例放置在其他类的字段中(例如,在A类的x.a中作为B类的实例的x.a$a.1)只是我实现封装的一种方式。但你完全正确,这种方式无法真正区分公共和私有方法,因为最终调用方法的是a.1而不是x.a。我会考虑一个好的更新示例来使事情更清晰。 - Rappster
2个回答

8
作为R中的一等对象,函数可以嵌套在另一个函数内部,如下所示:
hello <- function() {
    print_ <- function() { 
         return ('hello world')
    }
    print_()
}

是的,这很狡猾,可能不是最干净的方法,但它确实有效...使用“hello()”调用。


3
我不认为这是无礼的。这在JavaScript库中很常见,而且我不会惊讶地看到它出现在其他面向函数的脚本语言中。 - 16807

3
简短的回答是制作一个程序包。R 的对象系统和它分割代码(命名空间)的方式与类 Java 语言中的相应部分更加独立。
制作程序包时,您需要使用指令exportexportMethods在名为 NAMESPACE 的文件中指定要导出的内容。您可以选择不导出方法和其他您希望成为程序包私有的 R 对象(使用 Java 术语)。请参见 Writing R Extensions manual 中的“具有 S4 类和方法的命名空间”部分。
第一次制作程序包可能会有些棘手,但有很多帮助文档可供参考。请参见上面链接的 package.skeleton 和“编写 R 扩展”手册。
请确保您真正需要引用类。通常,常规S4类是更符合R语言的方式,不管它值得多少。关于R的许多面向对象构造(以及打包),Hadley Wickham的devtools wiki是一个很好的信息来源。

3
我完全同意这一点,包是正确的选择。但请注意,不从包中导出某些内容并不意味着用户无法访问它们。您仍然可以使用 ::: 运算符访问“私有”函数和数据。 - Gabor Csardi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接