1

我知道 R 中不同 OO 系统的一般来龙去脉,我愿意随时/尽可能长时间地呆在 S3 中。

但是,在过去的专业项目中,多次调度的需求一直困扰着我,并一直把我拉进 S4。

但我拒绝接受我找不到多调度的 S3仿真à la S4 至少能够支持我避免非系统数据漂移的主要用例:

  1. 我的分析程序创建了我发布给利益相关者的数据结构,供他们作为某些数据管道的一部分使用
  2. 通常我发布到诸如 MongoDB 之类的 DBMS,因此我的结构在某种程度上表现出来,并且可以/应该被认为是 DB 模式
  3. 我遵循“尽早发布,经常发布”的方法来保持敏捷,所以“事情会发生变化”是很自然的——经常而且随着时间的推移
  4. 随着数据结构的最终发展,我希望明确描述结构,以便我可以定义内部迁移例程和/或让利益相关者了解他们在数据库中处理的数据结构版本

如果我通过一些版本控制方案(这是迄今为止我能想到的最好的方案)来描述我的数据结构,我基本上需要将我的方法基于关于方法分派的三个签名参数:

  1. 一个用于给定数据转换任务的输入(类)
  2. 一个用于输入(类)的版本
  3. 一个用于输出的版本(类)

尽职调查

除了我读过的OO 领域指南:

我的方法

基于顺序 S3 的 aninput_class和 an方法output_class都可以随着时间的推移在结构上演变,由各自的版本类(例如v1等)捕获v2

第 1 步:定义必要的基础组件

# Generic that dispatches on `x`
new_output_class <- function(x, schema_version_in, schema_version_out, ...) {
  UseMethod("new_output_class", x)
}

# Generic that dispatches on 'schema_version_in' and 
# can thus be used to define distinct methods for 
# different versions of 'input_class'
new_output_class__input_class <- function(x, schema_version_in, 
  schema_version_out, ...) {
  UseMethod("new_output_class__input_class", schema_version_in)
}

# Generic that dispatches on 'schema_version_out' and 
# can thus be used to define distinct methods for 
# different versions of 'output_class' for inputs of class `input_class`
# with version class `v1`
new_output_class__input_class__v1 <- function(x, schema_version_in, 
  schema_version_out, ...) {
  UseMethod("new_output_class__input_class__v1", schema_version_out)
}

# Top-level method for dispatch based on `input_class`
new_output_class.input_class <- function(
  x, 
  schema_version_in,
  schema_version_out
) {
  message("Level 1: method for `input_class`")

  new_output_class__input_class(x, schema_version_in, schema_version_out)
}

input_class = v1第 2 步:为和的初始情况定义方法output_class = v1

library(magrittr)
library(stringr)

# Method for `input_class` of version `v1`
new_output_class__input_class.v1 <- function(
  x, 
  schema_version_in,
  schema_version_out
) {
  # message("Sub-level 1: `input_class/in:v1`")
  message(stringr::str_glue("Level 2: method for `input_class/in:{schema_version_in}`"))

  new_output_class__input_class__v1(x, schema_version_in, schema_version_out)
}

# Method for `output_class` of version `v1` for inputs 
# of `input_class` of version `v1`
new_output_class__input_class__v1.v1 <- function(
  x, 
  schema_version_in,
  schema_version_out
) {
  message(stringr::str_glue("Level 3: method for `input_class/in:{schema_version_in}/out:{schema_version_out}`"))

  structure(x, 
    class = c(stringr::str_glue("output_class_{schema_version_out}"),
      "output_class")
  )
}

x <- structure(letters[1:3], class = "input_class")

schema_version_of_x <- structure("v1", class = "v1")
schema_version_of_new_output <- structure("v1", class = "v1")

new_output_class(x, schema_version_of_x, schema_version_of_new_output)
# Level 1: method for `input_class`
# Level 2: method for `input_class/in:v1`
# Level 3: method for `input_class/in:v1/out:v1`
# [1] "a" "b" "c"
# attr(,"class")
# [1] "output_class_v1" "output_class"   

output_class第 3 步:处理to的演变v2

# Method for `output_class` of version `v2` for inputs 
# of `input_class` of version `v1`
new_output_class__input_class__v1.v2 <- function(
  x,
  schema_version_in,
  schema_version_out
) {
  message(stringr::str_glue("Level 3: method for `input_class/in:{schema_version_in}/out:{schema_version_out}`"))

  structure(x %>% rep(2),
    class = c(stringr::str_glue("output_class_{schema_version_out}"),
      "output_class")
  )
}

schema_version_of_new_output <- structure("v2", class = "v2")
new_output_class(x, schema_version_of_x, schema_version_of_new_output)
# Level 1: method for `input_class`
# Level 2: method for `input_class/in:v1`
# Level 3: method for `input_class/in:v1/out:v2`
# [1] "a" "b" "c" "a" "b" "c"
# attr(,"class")
# [1] "output_class_v2" "output_class"   

input_class第 4 步:处理to的演变v2

# Generic for input `input_class` of version `v2`
new_output_class__input_class__v2 <- function(x, schema_version_in, 
  schema_version_out, ...) {
  UseMethod("new_output_class__input_class__v2", schema_version_out)
}

# Method for `input_class` of version `v2`
new_output_class__input_class.v2 <- function(
  x, 
  schema_version_in,
  schema_version_out
) {
  message(stringr::str_glue("Level 2: method for `input_class/in:{schema_version_in}`"))

  new_output_class__input_class__v2(x, schema_version_in, schema_version_out)
}

# Method for `output_class` of version `v1` for inputs 
# of `input_class` of version `v2`
new_output_class__input_class__v2.v1 <- function(
  x, 
  schema_version_in,
  schema_version_out
) {
  message(stringr::str_glue("Level 3: method for `input_class/in:{schema_version_in}/out:{schema_version_out}`"))

  structure(x %>% tolower(),
    class = c(stringr::str_glue("output_class_{schema_version_out}"),
      "output_class")
  )
}

x <- structure(LETTERS[1:3], class = "input_class")
schema_version_of_x <- structure("v2", class = "v2")
schema_version_of_new_output <- structure("v1", class = "v1")
new_output_class(x, schema_version_of_x, schema_version_of_new_output)
# Level 1: method for `input_class`
# Level 2: method for `input_class/in:v2`
# Level 3: method for `input_class/in:v2/out:v1`
# [1] "a" "b" "c"
# attr(,"class")
# [1] "output_class_v1" "output_class"   

# Method for `output_class` of version `v2` for inputs 
# of `input_class` of version `v2`
new_output_class__input_class__v2.v2 <- function(
  x, 
  schema_version_in,
  schema_version_out
) {
  message(stringr::str_glue("Level 3: method for `input_class/in:{schema_version_in}/out:{schema_version_out}`"))

  structure(x %>% tolower() %>% rep(2),
    class = c(stringr::str_glue("output_class_{schema_version_out}"),
      "output_class")
  )
}

schema_version_of_x <- structure("v2", class = "v2")
schema_version_of_new_output <- structure("v2", class = "v2")
new_output_class(x, schema_version_of_x, schema_version_of_new_output)
# Level 1: method for `input_class`
# Level 2: method for `input_class/in:v2`
# Level 3: method for `input_class/in:v2/out:v2`
# [1] "a" "b" "c" "a" "b" "c"
# attr(,"class")
# [1] "output_class_v2" "output_class"  

优点

  1. 有用
  2. 这是S3
  3. 是系统的

缺点

  1. 它比任何基于 S4 的解决方案都更加冗长
  2. 我一点也不喜欢

有没有人想出更好的 S3 中的多调度仿真?

4

0 回答 0