槽赋值:`@`vs`slot()`vs`setReplaceMethod()`

amc*_*amc 6 r s4

我正在编写我的第一个R包,我试图找出为S4对象中的插槽赋值的最佳方法,记住最终用户不应该对S4类结构的细节大惊小怪正在使用.以下哪项最好?

  1. 直接访问插槽使用object@MySlot <- value:

    我理解这种不好的做法(例如,这个Q&A).

  2. 使用slot(object, "MySlot") <- value:

    R帮助表示在获取值时没有检查,但是在设置时检查(假设check尚未设置FALSE).这对我来说听起来很合理,并且让我觉得这是一个很好的方法,因为我不需要编写我自己的get/set方法,如下所示.

  3. 使用自定义方法setReplaceMethod():

    这种方法与上面的第二种方法相比如何?生成必要的get/set方法需要做更多的工作,但我可以更明确地确保写入插槽的值对该插槽类型有效.

    setGeneric("MySlot", function(object) {
        standardGeneric("MySlot")
    })
    
    setMethod("MySlot",
          signature = "MyClass",
          definition = function(object) {
              return(object@MySlot)
    })
    
    setGeneric("MySlot<-",
           function(object, value) {
               standardGeneric("MySlot<-")
    })
    
    setReplaceMethod("MySlot",
                 signature="MyClass",
                 function(object, value) {
                     object@MySlot<- value
                     validObject(object) # could add other checks
                     return(object)
    })
    
    Run Code Online (Sandbox Code Playgroud)

Rob*_*ski 4

根据定义,“不必担心 S4 类结构的细节”意味着最终用户不必了解您的插槽。因此,您在步骤 2 和 3 中编写的任何包装器都更多地用于内部一致性检查。我认为更重要的是检查您认为使用单元测试完整性检查会失败的边缘情况。

正如您所指出的,#1 可以很容易地被排除,并且应该只在内部方法中使用。无论你鼓励#2还是实施#3取决于变量的内容和个人品味,但我会鼓励后者。例如,如果您有一个logical标志,则可以使用 #2,但更具描述性的是enableFoo()。话虽这么说,如果您必须考虑开发人员时间(这在现实生活中几乎总是如此),您应该简要考虑一下将变异器降级为slot<-可能不会经常访问的成员之间的权衡(例如,访问次数少于1% 的用户),而不是像 #3 那样为所有内容实现自定义变异器。

最后,由于几乎所有三个 R 的 OOP 系统本质上都是语法糖,并且在语义上并没有真正受到语言的尊重(只有 S4 可以声明一些例外),因此很容易忘记面向对象编程背后的基本思想,如其他语言:在方法之外执行的任何代码都不应该知道对象的成员。您正在向世界提供一个外部接口,它是并且应该是一个黑匣子。