有没有办法为 S4 参考类声明公共和私有方法?

Posted

技术标签:

【中文标题】有没有办法为 S4 参考类声明公共和私有方法?【英文标题】:Is there a way to declare public and private methods for S4 Reference Classes? 【发布时间】:2012-06-19 20:33:18 【问题描述】:

预先说明:我知道 R 是一种函数式语言,所以请不要咬 ;-)

在我的许多程序中使用 OOP 方法时,我有很棒的经验。 现在,我想知道在 R 中使用 S4 Reference Classes 时是否有办法区分 publicprivate 方法?

示例

类定义

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

注意

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

    这样,类 def 可以保持清晰的排列,并且在单个方法 def 变得非常大的情况下更易于阅读。 它允许您随时切换到功能方法的执行。成为x 某个类的实例,您可以调用foo_ref(.self=x) 而不是x$foo()。 它允许您通过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")

公共与私人

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

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

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

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

知道如何指定这一点吗?

到目前为止我唯一想到的:

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

【问题讨论】:

x.a 是 A 类的实例,但 x.a$a.1 是 B 类的实例。您想阻止 B 类的实例访问 B 类的私有方法吗?你可能会进入一个痛苦的世界,试图阻止一个类访问它的方法,这取决于它可能发生在什么样的数据结构中...... 完全正确,这不是我的目标。同样,这是一个我觉得我只是缺乏一些关于 OOP 的背景知识的话题。将某些类的实例放在其他类的字段中(即,x.a$a.1 作为类x.aA 的类B 的实例)只是我实现某种程度封装的方式。但是您完全正确,这种方式实际上不可能区分公共方法和私有方法,因为最终调用该方法的是a.1,而不是x.a。我会考虑对我的示例进行很好的更新,以使事情更清楚。 【参考方案1】:

由于函数是 R 中的一等对象,您可以将一个嵌入另一个对象中,如下所示:

hello <- function() 
    print_ <- function()  
         return ('hello world')
    
    print_()

是的,它很厚颜无耻,可能不是最干净的方式,但它确实有效...使用 'hello()' 调用。

【讨论】:

我一点也不觉得这很厚脸皮。这在 javascript 库中是相当标准的做法,在其他面向函数的脚本语言中看到它我不会感到惊讶。【参考方案2】:

简短的回答是制作一个包。 R's object systems 和它的分区代码(命名空间)方式比它们在类 Java 语言中的等价物更加独立。

当您制作一个包时,您可以使用指令exportexportMethods 指定在名为NAMESPACE 的文件中导出的内容。您可以选择不导出您希望包私有的方法和其他 R 对象(使用 Java 术语)。见Namespaces with S4 classes and methods section of the Writing R Extensions manual

第一次制作包很棘手,但有很多帮助。请参阅package.skeleton 的文档和上面链接的编写 R 扩展手册。

确保引用类确实是您想要的。常规的 S4 课程通常是更 R-ish 的方式,无论其价值如何。 Hadley Wickham's devtools wiki 上提供了有关 R 的许多 OO 构造(以及打包)的重要信息来源。

【讨论】:

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

以上是关于有没有办法为 S4 参考类声明公共和私有方法?的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中调用私有方法

有没有办法从 javascript 类声明中反映公共实例类字段?

VB.Net 属性 - 公共获取,私有集

在 Raku 中测试私有方法

有没有办法从 Ruby 中的实例调用私有类方法?

有没有办法使用反射类设置私有/受保护的静态属性?