实际问题
免责声明
我知道R主要是一种功能语言。然而,确实也内置了非常强大的面向对象功能。加:我不认为模仿OOD原则/行为有什么问题。
上下文
我的web应用程序的R原型需要是“完全堆栈”/self、足够的和,以尽可能接近于设计我们的生产语言(C#/.NET)中使用的模式/原则和依赖注入容器 (R中简单DI概念的证明)。
在这方面,我非常喜欢接口(或抽象类)的使用,以便将代码模块解耦,并遵守面向对象设计的固体原理的 (依赖反演原理) (详细解释由“Bob叔叔”编写)。
尽管R6不显式地支持接口,但我仍然可以很好地使用只定义“抽象方法”的R6类来模仿(参见下面的示例)。这在很大程度上帮助我将我的软件设计传达给我们那些不太熟悉R的OO程序员,我为他们的“概念转换努力”而奋斗。
但是,我需要放弃inherit在R6Class中的值,因为当我实际上想要继承其他具体类(而不是“类似于抽象”的模拟接口类)时,这会成为一个小问题,因为这意味着在inherit中定义的不是一个类,而是两个类。
示例
依赖反转之前的:
Foo依赖于具体的类Bar。从OOD原则的角度来看,这是非常糟糕的,因为它会导致代码紧密耦合。
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" 依赖反转后的:
Foo和Bar现在是解耦的。两者都依赖于类IBar模拟的接口。我可以决定在运行时插入到Foo实例的接口的哪个实现(通过属性注入实现:字段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中的对象是)。它需要知道的是它可以对该对象调用哪些方法。为了了解这一点,只需知道字段中的对象实现的接口(在我们的例子中,用方法doSomething()实现的IBar)就足够了。
使用基类继承简化设计:
到现在为止还好。但是,我想也希望通过定义一些其他具体类可以继承的具体基类来简化我的设计。
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结合了“接口实现”和基类继承:
这就是我需要多重继承的地方,所以这样的东西可以工作(伪代码):
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的价值已经被用来模拟接口实现的“只是”,因此我失去了继承对实际具体类的“实际”好处。
另类思维:
或者,在某种程度上明确支持接口和具体类之间的区别将是很好的。例如,像这样的事情
Bar <- R6Class("Bar", implement = IBar, inherit = BaseClass,
private = list(x = letters)
)发布于 2016-02-16 13:18:53
对感兴趣的人:
我重新考虑了一下,意识到我想要/需要的并不是真正的多重继承本身,而是更好地模拟接口/抽象类的使用,而不是为此放弃inherit。
所以我尝试了有一点,这样我就可以在调用R6Class时区分inherit和implement。
也许这是个坏主意的原因有很多,但就目前而言,它可以完成任务;-)
您可以从我的叉状枝安装经过调整的版本。
示例
devtools::install_github("rappster/R6", ref = "feat_interface")
library(R6)正确实现接口和“标准继承”:
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当接口未正确实现时(即方法未实现):
Bar <- R6Class("Bar", implement = IFoo,
private = list(x = letters)
)
> Bar$new()
Error in Bar$new() :
Non-implemented interface method: foo依赖注入概念的证明
这是个小草稿,它详细阐述了R6中接口和依赖关系反转的动机和可能的实现方法。
发布于 2016-02-15 17:32:07
当你知道你是在为面向对象的语言(如C#、Java等)做原型的时候,我不认为模仿OOD原则/行为有什么问题。
它的问题是,你需要问这个问题,因为R仅仅是一个不足以建立一个OOD系统原型的工具,因为它不支持你所需要的。
或者只是原型那些依赖于数据分析的解决方案的方面,而不是原型那些不适合于范例的API方面。
也就是说,R的优点在于您可以编写自己的对象系统;毕竟,这就是R6。R6恰好不适合您的目的,但是没有什么能阻止您实现自己的系统。特别是,S3已经允许多重继承,它只是不支持编码的接口(相反,它们是临时的)。
但是,没有什么可以阻止您提供一个执行此编码的包装器函数。例如,您可以实现一组函数interface和class (不过要小心名称冲突),这些函数可以如下所用:
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))这将生成(例如):
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也做了类似的事情(但在我看来很糟糕)。
https://stackoverflow.com/questions/35414576
复制相似问题