R6类的多重继承 [英] Multiple inheritance for R6 classes

查看:120
本文介绍了R6类的多重继承的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

要解决 R6 不支持我知道R主要是一种功能语言.但是,它确实也内置了非常强大的面向对象.另外:当您

I know that R is primarily a functional language. However, it does also have very powerful object-orientation built in. Plus: I don't see what's wrong with mimicking OOD principles/behavior when you

  1. 知道,您正在制作原型以使用面向对象的语言,例如C#,Java等.

  1. know you're prototyping for an object-oriented language such as C#, Java, etc.

您的应用程序原型需要自给自足(完整堆栈",包括数据库后端,业务逻辑和前端/UI)

your prototypes of apps need to be self-sufficient ("full stack" including DB-backends, business logic and frontends/UI)

您拥有诸如R6之类的伟大的原型技术,并且可以随时使用

you have such great "prototyping technology" like R6 and shiny at your disposal

上下文

我用于Web应用程序的R原型必须既全栈"/自给自足,并且尽可能与设计模式/原理和

Context

My R prototypes for web apps need to be both "full stack"/self sufficient and as close as possible to design patterns/principles and dependency injection containers (proof of concept of simple DI in R) used in our production language (C#/.NET).

在这方面,我非常喜欢使用接口(或抽象类)来分离代码模块并遵守 D (.

In that regard, I came to like the use of interfaces (or abstract classes) very much in order to decouple code modules and to comply with the D (dependency inversion principle) of the SOLID principles of OOD (detailed explanation by "Uncle Bob").

即使R6不明确支持接口,我仍然可以使用定义了抽象方法"的R6类来完美地 mimick (请参见下面的示例).这可以帮助我与不太熟悉的OO-程序员交流我的软件设计.

Even though R6 does not explicitly support interfaces, I can nevertheless perfectly mimick them with R6 classes that define nothing but "abstract methods" (see example below). This helps me a lot with communicating my software designs to our OO-programmers that aren't very familiar with R. I strive for as little "conceptional conversion effort" on their part.

但是,我需要放弃R6Classinherit的值,因为当我实际上想从其他 concrete 继承时(相对于"类似于的模拟接口类",因为这将意味着在inherit中定义一个而不是两个类.

However, I need to give up my value for inherit in R6Class for that which becomes a bit of a problem when I actually want to inherit from other concrete (as opposed to "abstract-like" mimicked interface classes) because this would mean to define not one but two classes in inherit.

反转依赖项之前:

Foo取决于具体的类Bar.从OOD原则的角度来看,这很糟糕,因为它导致代码紧密耦合.

Foo depends on concrete class Bar. From an OOD principles' view, this is pretty bad as it leads to code being tightly coupled.

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的实例中(通过属性注入实现:Foo的字段bar )

Foo and Bar are decoupled now. Both depend on an interface which is mimicked by class IBar. I can decide which implementation of that interface I would like to plug in to instances of Foo at runtime (realized via Property Injection: field bar of Foo)

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中的对象的实现方式 >.它只需要知道可以在该对象上调用的方法即可.为了知道这一点,只需知道字段bar中的对象实现的接口(在我们的示例中为IBar,使用方法doSomething()).

A bit mor on why this makes sense with regard to OOD: Foo should be completely agnostic of the the way the object stored in field bar is implemented. All it needs to know is which methods it can call on that object. And in order to know that, it's enough to know the interface that the object in field bar implements (IBar with method doSomething(), in our case).

使用基类的继承来简化设计:

到目前为止,太好了.但是,我也想通过定义某些 concrete 基类来简化我的设计,这些基类是我的其他 concrete 类中的某些类可以从中继承. /p>

So far, so good. However, I'd also like to simplify my design by definining certain concrete base classes that some of my other concrete classes can inherit from.

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

结合接口实现"和基本分类继承:

这是我需要多重继承的地方,因此类似的东西可以工作(伪代码):

This is where I would need multiple inheritance so something like this would work (PSEUDO CODE):

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的值已经被仅仅"用来模仿接口实现,因此对于实际的具体类,我失去了继承的实际"好处.

Currently, my value for inherit is already being used up "just" for mimicking an interface implementation and so I lose the "actual" benefits of inheritance for my actual concrete classes.

替代思想:

或者,最好以某种方式显式支持 interface concrete 类之间的区别.例如这样的东西

Alternatively, it would be great to explicitly support a differentiation between interface and concrete classes somehow. For example something like this

Bar <- R6Class("Bar", implement = IBar, inherit = BaseClass,
  private = list(x = letters)
)

推荐答案

对于那些感兴趣的人:

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

I gave it a second thought and realized that's it's not really multiple inheritance per se that I want/need, but rather some sort of better mimicking the use of interfaces/abstract classes without giving up inherit for that.

因此,我尝试了稍作调整R6 ,这样我就可以区分在调用R6Classinheritimplement之间.

So I tried tweaking R6 a bit so it would allow me to distinguish between inherit and implement in a call to R6Class.

这是一个坏主意的大概原因,但目前,它已经完成了工作;-)

Probably tons of reasons why this is a bad idea, but for now, it gets the job done ;-)

您可以从我的分叉分支中安装调整后的版本.

You can install the tweaked version from my forked branch.

devtools::install_github("rappster/R6", ref = "feat_interface")
library(R6)

正确实现接口标准继承":

Correct implementation of interface and "standard inheritance":

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

如果接口未正确实现(即方法未实现):

When an interface is not implemented correctly (i.e. method not implemented):

 Bar <- R6Class("Bar", implement = IFoo,
    private = list(x = letters)
  )
> Bar$new()
Error in Bar$new() : 

Non-implemented interface method: foo

依赖注入的概念证明

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

Proof of concept for dependency injection

This is a little draft that elaborates a bit on the motivation and possible implementation approaches for interfaces and inversion of dependency in R6.

这篇关于R6类的多重继承的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆