oop – 在R的S4中定义具有多个可选空槽的类的有效方法?

我正在构建一个包来处理最多4种不同类型的数据.这些类型中的每一种都是矩阵,data.frame或树形式的合法类.根据处理数据的方式和其他实验因素,这些数据组件中的一些可能会丢失,但是能够将此信息存储为特殊类的实例并且具有识别不同组件的方法仍然非常有用.数据.

方法1:

我已经尝试了一种看起来像嵌套树的增量继承结构,其中每种数据类型组合都有自己明确定义的类.对于未来的其他数据类型来说,这似乎很难扩展,并且对于新开发人员来说,学习所有类名也很困难,无论这些名称组织得井井有条.

方法2:

第二种方法是创建一个“主类”,其中包括所有4种数据类型的槽.为了使缺失数据的实例的插槽为NULL,似乎有必要首先在NULL类和新数据类型类之间定义虚拟类联合,然后使用虚拟类联合作为预期的类.大师班的相关插槽.这是一个示例(假设已经定义了每个数据类型类):

################################################################################
# Use setClassUnion to define the unholy NULL-data union as a virtual class.
################################################################################    
setClassUnion("dataClass1OrNULL", c("dataClass1", "NULL"))
setClassUnion("dataClass2OrNULL", c("dataClass2", "NULL"))
setClassUnion("dataClass3OrNULL", c("dataClass3", "NULL"))
setClassUnion("dataClass4OrNULL", c("dataClass4", "NULL"))
################################################################################
# Now define the master class with all 4 slots, and 
# also the possibility of empty (NULL) slots and an explicity prototype for
# slots to be set to NULL if they are not provided at instantiation.
################################################################################
setClass(Class="theMasterClass", 
    representation=representation(
        slot1="dataClass1OrNULL",
        slot2="dataClass2OrNULL",
        slot3="dataClass3OrNULL",
        slot4="dataClass4OrNULL"),
    prototype=prototype(slot1=NULL, slot2=NULL, slot3=NULL, slot4=NULL)
)
################################################################################

所以这个问题可以改为:

这些方法中是否有更有效和/或更灵活的替代方案?

此示例从对SO question about setting the default value of slot to NULL的答案进行了修改.这个问题的不同之处在于我有兴趣知道R中用于创建具有插槽的类的最佳选项,如果需要可以为空,尽管要求所有其他非空的特定复杂类案例.

最佳答案 在我看来…

方法2

它有点违背了采用正式类系统的目的,然后创建一个包含不明确定义的槽(‘A’或NULL)的类.至少我会尝试使DataClass1具有类似“NULL”的默认值.举个简单的例子,这里的默认值是零长度数值向量.

setClass("DataClass1", representation=representation(x="numeric"))
DataClass1 <- function(x=numeric(), ...) {
    new("DataClass1", x=x, ...)
}

然后

setClass("MasterClass1", representation=representation(dataClass1="DataClass1"))
MasterClass1 <- function(dataClass1=DataClass1(), ...) {
    new("MasterClass1", dataClass1=dataClass1, ...)
}

这样做的一个好处是方法不必测试插槽中的实例是NULL还是’DataClass1′

setMethod(length, "DataClass1", function(x) length(x@x))
setMethod(length, "MasterClass1", function(x) length(x@dataClass1))

> length(MasterClass1())
[1] 0
> length(MasterClass1(DataClass1(1:5)))
[1] 5

为了回应你关于警告用户访问“空”插槽的评论,并记住用户通常希望函数做某事而不是告诉他们他们做错了什么,我可能会准确地返回空对象DataClass1()反映了对象的状态.也许show方法会提供一个概述,增强了插槽的状态 – DataClass1:none.如果MasterClass1代表一种协调几种不同分析的方式,这似乎是特别合适的,用户可能只做一些分析.

这种方法(或方法2)的局限性在于您没有获得方法调度 – 您不能编写仅适用于具有非零长度的DataClass1实例且被强制执行的实例的方法某种手动调度(例如,使用if或switch).这似乎是对开发人员的限制,但它也适用于用户 – 用户无法了解哪些操作特别适合具有非零长度DataClass1实例的MasterClass1实例.

方法1

当你说层次结构中的类的名称会让你的用户感到困惑时,似乎这可能指向一个更基本的问题 – 你要努力做出全面的数据类型表示;用户永远无法跟踪ClassWithMatrixDataFrameAndTree,因为它不代表他们查看数据的方式.这可能是一个缩小你的野心的机会,只能真正解决你正在调查的地区最突出的部分.或者也许是一个重新思考用户如何思考并与他们收集的数据进行交互的机会,以及使用界面(用户看到的)与实现的分离(您如何选择表示数据)类)由类系统提供,以更有效地封装用户可能做的事情.

把命名和数量放在一边,当你说“将来难以扩展其他数据类型”时,我想知道S4类的某些细微差别是否会让你绊倒?简短的解决方案是避免编写自己的初始化方法,并依赖构造函数来完成棘手的工作,

setClass("A", representation(x="numeric"))
setClass("B", representation(y="numeric"), contains="A")

A <- function(x = numeric(), ...) new("A", x=x, ...)
B <- function(a = A(), y = numeric(), ...) new("B", a, y=y, ...)

然后

> B(A(1:5), 10)
An object of class "B"
Slot "y":
[1] 10

Slot "x":
[1] 1 2 3 4 5
点赞