就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the help center寻求指导。
9年前关闭。
如何在函数式编程中使用多态性(使用动态类型系统)?
让我们考虑以下示例(首先在 OOP 中,然后在 FP 中)。该程序非常简单 - 有图形列表,我们需要绘制所有这些图形,不同的图形使用不同的绘图算法。
在 OOP 中可以轻松完成,但如何在 FP 中完成呢?特别是在具有动态类型系统的语言中,如 Scheme、Clojure(在编译时没有静态类型解析)?
我创建了简单的代码(实时版本 http://tinkerbin.com/0C3y8D9Z,按“运行”按钮)。我在 FP 示例中使用了 if/else 开关,但这是一种非常糟糕的方法。如何更好地解决这样的问题?
示例是用 JavaScript 编写的,但这只是为了简单起见,如果看到任何具有动态类型系统的函数式语言的解决方案将会很有趣。
面向对象
var print = function(message){document.write(message + "\n<br/>")}
// Object Oriented Approach.
var circle = {
draw: function(){print("drawing circle ...")}
}
var rectangle = {
draw: function(){print("drawing rectangle ...")}
}
var objects = [circle, rectangle]
objects.forEach(function(o){
o.draw()
})
FP
var print = function(message){document.write(message + "\n<br/>")}
// Functional Approach.
var circle = {type: 'Circle'}
var drawCircle = function(){print("drawing circle ...")}
var rectangle = {type: 'Rectangle'}
var drawRectangle = function(){print("drawing rectangle ...")}
var objects = [circle, rectangle]
objects.forEach(function(o){
if(o.type == 'Circle') drawCircle(o)
else if(o.type == 'Rectangle') drawRectangle(o)
else throw new Error('unknown type!')
})
最佳答案
您的“FP”版本不是我认为惯用的 FP 示例。在 FP 中,您经常使用变体和模式匹配,而在 OOP 中您将使用类和方法分派(dispatch)。特别是,您只有一个 draw
已经在内部进行调度的函数:
var circle = {type: 'Circle'}
var rectangle = {type: 'Rectangle'}
var draw = function(shape) {
switch (shape.type) {
case 'Circle': print("drawing circle ..."); break
case 'Rectangle': print("drawing rectangle ..."); break
}
}
var objects = [circle, rectangle]
objects.forEach(draw)
(当然,那是 JavaScript。在函数式语言中,你通常有更优雅和简洁的语法,例如:
draw `Circle = print "drawing circle..."
draw `Rectangle = print "drawing rectangle..."
objects = [`Circle, `Rectangle]
foreach draw objects
)
现在,一般的 OO 爱好者会看到上面的代码并说:“但是 OO 解决方案是可扩展的,上面的不是!”从某种意义上说,您可以轻松地将新形状添加到 OO 版本中,而无需触及任何现有形状(或它们的
draw
函数),这是正确的。使用 FP 方式,您必须进入并扩展 draw
功能,以及可能存在的所有其他操作。但那些人没有看到的是,反过来也是正确的:FP 解决方案是可扩展的,而 OO 解决方案则不然!也就是说,当您在现有形状上添加新操作时,您无需触及任何形状定义,也无需触及现有操作。您只需添加另一个函数,而对于 OO,您必须修改每个类或构造函数以包含新操作的实现。
也就是说,就模块化而言,这里存在二元论。沿着两个轴实现同时可扩展性的理想在文献中被称为“表达式问题”,虽然存在各种解决方案(尤其是在函数式语言中),但它们通常更复杂。因此,在实践中,您通常希望决定一个维度,这取决于哪个维度更可能对手头的问题很重要。
功能版本还有其他优点。例如,它可以简单地扩展到多调度或更复杂的案例区分。在实现复杂的算法并且不同情况相互关联的情况下,它也是更可取的,这样您就希望将代码放在一个地方。根据经验,每当您开始在 OO 中使用访问者模式时,函数式解决方案会更合适(而且容易得多)。
一些进一步的说明:
关于oop - 如何在函数式编程中使用多态性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13478064/