Multiple inheritance for R6 classes

半世苍凉 提交于 2019-12-02 22:24:10

For those interested:

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.

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.

Example

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

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.

Plus: I don't see what's wrong with mimicking OOD principles/behavior when you know you're prototyping for an object-oriented language such as C#, Java, etc.

What’s wrong with it is that you needed to ask this question because R is simply an inadequate tool to prototype an OOD system, because it doesn’t support what you need.

Or just prototype those aspects of your solution which rely on data analysis, and don’t prototype those aspects of the API which don’t fit into the paradigm.

That said, the strength of R is that you can write your own object system; after all, that’s what R6 is. R6 just so happens to be inadequate for your purposes, but nothing stops you from implementing your own system. In particular, S3 already allows multiple inheritance, it just doesn’t support codified interfaces (instead, they happen ad-hoc).

But nothing stops you from providing a wrapper function that performs this codification. For instance, you could implement a set of functions interface and class (beware name clashes though) that can be used as follows:

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))

This would then generate (for instance):

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'))

… and so on. In fact, S4 does something similar (but badly, in my opinion).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!