首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >R6类的多重继承

R6类的多重继承
EN

Stack Overflow用户
提问于 2016-02-15 16:41:23
回答 2查看 2.4K关注 0票数 12

实际问题

对于R6不支持多重继承这一事实,我有什么选择?

免责声明

我知道R主要是一种功能语言。然而,确实也内置了非常强大的面向对象功能。加:我不认为模仿OOD原则/行为有什么问题。

  1. 您是面向对象语言(如C#、Java等)的原型化
  2. 应用程序的原型必须是self-sufficient (“完整堆栈”包括DB-后端、业务逻辑和前端/UI)。
  3. 你拥有这样伟大的“原型技术”,就像R6一样,你可以随意使用。

上下文

我的web应用程序的R原型需要是“完全堆栈”/self、足够的,以尽可能接近于设计我们的生产语言(C#/.NET)中使用的模式/原则和依赖注入容器 (R中简单DI概念的证明)。

在这方面,我非常喜欢接口(或抽象类)的使用,以便将代码模块解耦,并遵守面向对象设计的固体原理的 (依赖反演原理) (详细解释由“Bob叔叔”编写)。

尽管R6不显式地支持接口,但我仍然可以很好地使用只定义“抽象方法”的R6类来模仿(参见下面的示例)。这在很大程度上帮助我将我的软件设计传达给我们那些不太熟悉R的OO程序员,我为他们的“概念转换努力”而奋斗。

但是,我需要放弃inheritR6Class中的值,因为当我实际上想要继承其他具体类(而不是“类似于抽象”的模拟接口类)时,这会成为一个小问题,因为这意味着在inherit中定义的不是一个类,而是两个类。

示例

依赖反转之前的

Foo依赖于具体的类Bar。从OOD原则的角度来看,这是非常糟糕的,因为它会导致代码紧密耦合。

代码语言:javascript
复制
Bar <- R6Class("Bar",
  public = list(doSomething = function(n) private$x[1:n]),
  private = list(x = letters)
)
Foo <- R6Class("Foo",
  public = list(bar = Bar$new())
)
inst <- Foo$new()
> class(inst)
> class(inst$bar)
[1] "Bar" "R6" 

依赖反转后的

FooBar现在是解耦的。两者都依赖于类IBar模拟的接口。我可以决定在运行时插入到Foo实例的接口的哪个实现(通过属性注入实现:字段bar of Foo)

代码语言:javascript
复制
IBar <- R6Class("IBar",
  public = list(doSomething = function(n = 1) stop("I'm the inferace method"))
)
Bar <- R6Class("Bar", inherit = IBar,
  public = list(doSomething = function(n = 1) private$x[1:n]),
  private = list(x = letters)
)
Baz <- R6Class("Baz", inherit = IBar,
  public = list(doSomething = function(n = 1) private$x[1:n]),
  private = list(x = 1:24)
)
Foo <- R6Class("Foo",
  public = list(bar = IBar$new())
)

inst <- Foo$new()
inst$bar <- Bar$new()
> class(inst$bar)
[1] "Bar"  "IBar" "R6"  
> inst$bar$doSomething(5)
[1] "a" "b" "c" "d" "e"

inst$bar <- Baz$new()
[1] "Baz"  "IBar" "R6"  
> inst$bar$doSomething(5)
[1] 1 2 3 4 5

对于OOD为什么这是有意义的:Foo应该是完全不可知论的(存储在字段bar中的对象是)。它需要知道的是它可以对该对象调用哪些方法。为了了解这一点,只需知道字段中的对象实现的接口(在我们的例子中,用方法doSomething()实现的IBar)就足够了。

使用基类继承简化设计:

到现在为止还好。但是,我想也希望通过定义一些其他具体类可以继承的具体基类来简化我的设计。

代码语言:javascript
复制
BaseClass <- R6Class("BaseClass",
  public = list(doSomething = function(n = 1) private$x[1:n])
)
Bar <- R6Class("Bar", inherit = BaseClass,
  private = list(x = letters)
)
Baz <- R6Class("Bar", inherit = BaseClass,
  private = list(x = 1:24)
)

inst <- Foo$new()
inst$bar <- Bar$new()
> class(inst$bar)
[1] "Bar"       "BaseClass" "R6"   
> inst$bar$doSomething(5)
[1] "a" "b" "c" "d" "e"

inst$bar <- Baz$new()
> class(inst$bar)
[1] "Baz"       "BaseClass" "R6"       
> inst$bar$doSomething(5)
[1] 1 2 3 4 5

结合了“接口实现”和基类继承:

这就是我需要多重继承的地方,所以这样的东西可以工作(伪代码):

代码语言:javascript
复制
IBar <- R6Class("IBar",
  public = list(doSomething = function() stop("I'm the inferace method"))
)
BaseClass <- R6Class("BaseClass",
  public = list(doSomething = function(n = 1) private$x[1:n])
)
Bar <- R6Class("Bar", inherit = c(IBar, BaseClass),
  private = list(x = letters)
)
inst <- Foo$new()
inst$bar <- Bar$new()
class(inst$bar)
[1] "Bar"       "BaseClass" "IBar" "R6"

目前,我对inherit的价值已经被用来模拟接口实现的“只是”,因此我失去了继承对实际具体类的“实际”好处。

另类思维:

或者,在某种程度上明确支持接口和具体类之间的区别将是很好的。例如,像这样的事情

代码语言:javascript
复制
Bar <- R6Class("Bar", implement = IBar, inherit = BaseClass,
  private = list(x = letters)
)
EN

回答 2

Stack Overflow用户

发布于 2016-02-16 13:18:53

对感兴趣的人:

我重新考虑了一下,意识到我想要/需要的并不是真正的多重继承本身,而是更好地模拟接口/抽象类的使用,而不是为此放弃inherit

所以我尝试了有一点,这样我就可以在调用R6Class时区分inheritimplement

也许这是个坏主意的原因有很多,但就目前而言,它可以完成任务;-)

您可以从我的叉状枝安装经过调整的版本。

示例

代码语言:javascript
复制
devtools::install_github("rappster/R6", ref = "feat_interface")
library(R6)

正确实现接口“标准继承”:

代码语言:javascript
复制
IFoo <- R6Class("IFoo",
  public = list(foo = function() stop("I'm the inferace method"))
)
BaseClass <- R6Class("BaseClass",
  public = list(foo = function(n = 1) private$x[1:n])
)
Foo <- R6Class("Foo", implement = IFoo, inherit = BaseClass,
  private = list(x = letters)
)

> Foo$new()
<Foo>
  Implements interface: <IFoo>
  Inherits from: <BaseClass>
  Public:
    clone: function (deep = FALSE) 
    foo: function (n = 1) 
  Private:
    x: a b c d e f g h i j k l m n o p q r s t u v w x y z

当接口未正确实现时(即方法未实现):

代码语言:javascript
复制
 Bar <- R6Class("Bar", implement = IFoo,
    private = list(x = letters)
  )
> Bar$new()
Error in Bar$new() : 

Non-implemented interface method: foo

依赖注入概念的证明

这是个小草稿,它详细阐述了R6中接口和依赖关系反转的动机和可能的实现方法。

票数 8
EN

Stack Overflow用户

发布于 2016-02-15 17:32:07

当你知道你是在为面向对象的语言(如C#、Java等)做原型的时候,我不认为模仿OOD原则/行为有什么问题。

它的问题是,你需要问这个问题,因为R仅仅是一个不足以建立一个OOD系统原型的工具,因为它不支持你所需要的。

或者只是原型那些依赖于数据分析的解决方案的方面,而不是原型那些不适合于范例的API方面。

也就是说,R的优点在于您可以编写自己的对象系统;毕竟,这就是R6。R6恰好不适合您的目的,但是没有什么能阻止您实现自己的系统。特别是,S3已经允许多重继承,它只是不支持编码的接口(相反,它们是临时的)。

但是,没有什么可以阻止您提供一个执行此编码的包装器函数。例如,您可以实现一组函数interfaceclass (不过要小心名称冲突),这些函数可以如下所用:

代码语言:javascript
复制
interface(Printable,
    print = prototype(x, ...))

interface(Comparable,
    compare_to = prototype(x, y))

class(Foo,
    implements = c(Printable, Comparable),
    private = list(x = 1),
    print = function (x, ...) base::print(x$x, ...),
    compare_to = function (x, y) sign(x$x - y$x))

这将生成(例如):

代码语言:javascript
复制
print.Foo = function (x, ...) base::print(x$x, ...)

compare_to = function (x, y) UseMethod('compare_to')

compare_to.foo = function (x, y) sign(x$x - y$x)

Foo = function ()
    structure(list(x = 1), class = c('Foo', 'Printable', 'Comparable'))

…诸若此类。事实上,S4也做了类似的事情(但在我看来很糟糕)。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35414576

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档