自从我去年开始学习f#和OCaml以来,我已经阅读了大量的文章,这些文章坚持认为设计模式(尤其是Java中的)是命令式语言中缺失特性的变通方法。我发现的一篇文章给出了相当有力的主张:

Most people I've met have read the Design Patterns book by the Gang of Four (GoF). Any self respecting programmer will tell you that the book is language agnostic and the patterns apply to software engineering in general, regardless of which language you use. This is a noble claim. Unfortunately it is far removed from the truth. Functional languages are extremely expressive. In a functional language one does not need design patterns because the language is likely so high level, you end up programming in concepts that eliminate design patterns all together.

函数式编程(FP)的主要特性包括函数作为一类值、curry化、不可变值等。在我看来,OO设计模式是否接近这些特性并不明显。

此外,在支持OOP的函数式语言(如f#和OCaml)中,使用这些语言的程序员显然会使用与其他OOP语言相同的设计模式。事实上,现在我每天都在使用f#和OCaml,我在这些语言中使用的模式与我在Java中使用的模式之间没有明显的区别。

函数式编程消除了对面向对象设计模式的需求这一说法是否属实?如果是这样的话,你能发布或链接到一个典型的OOP设计模式的例子及其功能对等物吗?


当前回答

OOP和FP有不同的目标。OOP旨在封装软件组件的复杂/可移动部分,FP旨在最小化软件组件的复杂性和依赖性。

然而,这两种范式并不一定是100%矛盾的,可以一起应用,从两个世界中获得好处。

即使使用像c#这样原生不支持函数式编程的语言,如果您理解FP原则,您也可以编写函数式代码。同样地,如果你理解OOP原则、模式和最佳实践,你也可以使用f#应用OOP原则。无论您使用何种编程语言,您都可以根据您试图解决的情况和问题做出正确的选择。

其他回答

正如其他人所说,函数式编程有特定的模式。我认为摆脱设计模式的问题与其说是转换到功能的问题,不如说是语言特性的问题。

看看Scala是如何废除“单例模式”的:只需声明一个对象而不是一个类。 另一个特性,模式匹配,有助于避免笨重的访问者模式。对比如下: Scala的模式匹配=类固醇访问者模式

Scala和f#一样,是OO-functional的融合。我不了解f#,但它可能有这些特性。

闭包是在函数式语言中出现的,但不需要被限制在函数式语言中。它们有助于使用委托模式。

还有一个观察结果。这段代码实现了一个模式:它是如此经典,如此基本,以至于我们通常不认为它是一个“模式”,但它确实是:

for(int i = 0; i < myList.size(); i++) { doWhatever(myList.get(i)); }

像Java和c#这样的命令式语言已经采用了本质上是一个函数结构来处理这个问题:“foreach”。

Norvig的演示暗示了他们对所有GoF模式的分析,他们说23个模式中有16个在函数式语言中有更简单的实现,或者只是语言的一部分。因此,大概至少有7个要么是a)同样复杂,要么是b)没有出现在语言中。不幸的是,我们没有列举它们!

我认为很明显,GoF中的大多数“创造性”或“结构性”模式只是让Java或c++中的原始类型系统做你想做的事情的技巧。但是不管你用什么语言编程,其余的都是值得考虑的。

一个可能是Prototype;虽然它是JavaScript的基本概念,但它必须在其他语言中从头开始实现。

我最喜欢的模式之一是空对象模式:表示一个对象不做任何适当的事情。这可能更容易在函数式语言中建模。然而,真正的成就是视角的转变。

OOP和GoF模式处理状态。OOP对现实进行建模,使代码库尽可能接近现实的给定需求。GoF设计模式是为解决原子现实问题而确定的模式。它们以语义的方式处理状态问题。

因为在真正的函数式编程中不存在状态,所以应用GoF模式没有意义。功能设计模式与GoF设计模式是不同的。与现实相比,每个功能设计模式都是人为的,因为函数是数学的构造,而不是现实。

函数缺乏时间的概念,因为无论当前时间是多少,它们总是返回相同的值,除非时间是函数参数的一部分,这使得处理“未来的请求”非常困难。混合语言混合了这些概念,使得这些语言不是真正的函数式编程语言。

Functional languages are rising only because of one thing: the current natural restrictions of physics. Todays processors are limited in their speed of processing instructions due to physical laws. You see a stagnation in clock frequency but an expansion in processing cores. That's why parallelism of instructions becomes more and more important to increase speed of modern applications. As functional programming by definition has no state and therefore has no side effects it is safe to process functions safely in parallel.

GoF模式并没有过时。它们至少对真实世界的需求建模是必要的。但是如果你使用函数式编程语言,你必须将它们转换成它们的混合等价物。最后,如果使用持久性,就没有机会只编写函数式程序。对于程序的混合元素,仍然需要使用GoF模式。对于任何其他纯功能性的元素,没有必要使用GoF模式,因为没有状态。

因为GoF模式对于真正的函数式编程不是必需的,这并不意味着不应该应用SOLID原则。SOLID原则超越了任何语言范式。

Peter Norvig的《动态编程中的设计模式》(Design Patterns in Dynamic Programming)对这一主题进行了深入的讨论,不过他讲的是“动态”语言而不是“函数式”语言(两者有重叠)。

从本质上说,是的!

当一个模式绕过了缺失的特性(高阶函数、流处理……),最终促进了组合。 一遍又一遍地重写模式实现的需要本身可以被视为一种语言气味。

此外,这个页面(AreDesignPatternsMissingLanguageFeatures)提供了一个“模式/功能”转换表和一些不错的讨论,如果你愿意深入研究的话。