实际问题
免责声明
我知道 R 主要是一种函数式语言。但是,它也确实内置了非常强大的面向对象。另外:我看不出模仿 OOD 原则/行为有什么问题
知道您正在为 C#、Java 等面向对象的语言进行原型设计。
您的应用程序原型需要自给自足(“全栈”,包括数据库后端、业务逻辑和前端/UI)
您拥有像 R6 这样出色的“原型制作技术”,并且可以随意使用
语境
我的 Web 应用程序R原型需要“全栈”/自给自足,并尽可能接近我们的生产语言 (C#/.网)。
在这方面,我非常喜欢使用接口(或抽象类)来解耦代码模块并遵守OOD的SOLID原则的D(依赖倒置原则)(“鲍伯叔叔”的详细解释) )。
尽管 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
现在解耦了。两者都依赖于由 class 模仿的接口IBar
。我可以决定我想在运行时将该接口的哪个实现Foo
插入到实例中(通过属性注入实现:字段bar
)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()
使用从基类继承来简化设计:
到目前为止,一切都很好。但是,我还想通过定义某些我的其他具体类可以继承的具体基类来简化我的设计。
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)
)