自从我去年开始学习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设计模式的例子及其功能对等物吗?


当前回答

你引用的那篇博客文章有点言过其实了。FP并没有消除对设计模式的需求。术语“设计模式”在FP语言中并没有广泛用于描述相同的事情。但它们确实存在。函数式语言有很多最佳实践规则,比如“当你遇到问题X时,使用看起来像Y的代码”,这基本上就是设计模式。

然而,大多数特定于oop的设计模式在函数式语言中几乎是不相关的,这是正确的。

我不认为说设计模式一般只是为了弥补语言中的缺陷而存在是特别有争议的。 如果另一种语言可以简单地解决同样的问题,那么另一种语言就不需要设计模式了。这种语言的用户甚至可能没有意识到这个问题的存在,因为,好吧,这在那种语言中不是问题。

下面是“四人帮”对这个问题的看法:

The choice of programming language is important because it influences one's point of view. Our patterns assume Smalltalk/C++-level language features, and that choice determines what can and cannot be implemented easily. If we assumed procedural languages, we might have included design patterns called "Inheritance", "Encapsulation," and "Polymorphism". Similarly, some of our patterns are supported directly by the less common object-oriented languages. CLOS has multi-methods, for example, which lessen the need for a pattern such as Visitor. In fact, there are enough differences between Smalltalk and C++ to mean that some patterns can be expressed more easily in one language than the other. (See Iterator for example.)

(以上内容摘自《设计模式导论》一书第4页第3段)

功能的主要特点 编程包括以下函数 一流的价值观,咖喱, 不可变值等等。似乎不是这样 在我看来,OO设计模式是显而易见的 有接近这些吗 特性。

What is the command pattern, if not an approximation of first-class functions? :) In an FP language, you'd simply pass a function as the argument to another function. In an OOP language, you have to wrap up the function in a class, which you can instantiate and then pass that object to the other function. The effect is the same, but in OOP it's called a design pattern, and it takes a whole lot more code. And what is the abstract factory pattern, if not currying? Pass parameters to a function a bit at a time, to configure what kind of value it spits out when you finally call it.

所以,是的,在FP语言中,一些GoF设计模式是多余的,因为存在更强大、更容易使用的替代方案。

当然,仍然有一些设计模式是FP语言无法解决的。FP与单例的等价是什么?(暂时不考虑单例对象通常是一种糟糕的模式。)

这也是双向的。正如我所说,FP也有它的设计模式;人们只是通常不这么认为而已。

但是你可能遇到过单子。如果不是“处理全局状态”的设计模式,它们是什么?这个问题在面向对象语言中是如此简单,以至于没有相应的设计模式存在。

我们不需要“增加静态变量”或“从套接字读取”的设计模式,因为这就是你要做的。

说一个单子是一种设计模式,就像说整数和它们的常规操作和零元素是一种设计模式一样荒谬。不,单子是一种数学模式,不是设计模式。

在(纯)函数式语言中,副作用和可变状态是不可能的,除非你使用单子“设计模式”,或任何其他允许相同事情的方法来解决它。

此外,在函数式语言中 支持OOP(如f#和 OCaml),在我看来很明显 使用这些语言的程序员 是否会使用相同的设计模式 发现对所有其他OOP都可用 语言。事实上,现在我使用f# 和OCaml每天,没有 两者之间的显著差异 我在这些语言中使用的模式vs 我写字时使用的模式 Java。

也许是因为你的思维仍然是强制性的?很多人一生都在处理命令式语言,当他们尝试函数式语言时,很难放弃这个习惯。(我在f#中看到过一些非常有趣的尝试,实际上每个函数都只是一串“let”语句,基本上就像你使用了一个C程序,并将所有分号替换为“let”。:))

但另一种可能是,您还没有意识到您正在解决的问题很琐碎,这将需要OOP语言中的设计模式。

当您使用curry,或将一个函数作为参数传递给另一个函数时,请停下来想一想在OOP语言中如何做到这一点。

这种说法有道理吗 函数式编程消除了 需要面向对象设计模式?

是的。:) 当您使用FP语言工作时,您不再需要特定于oop的设计模式。但是您仍然需要一些通用的设计模式,如MVC或其他非oop特定的东西,并且您需要一些新的特定于fp的“设计模式”。所有语言都有其缺点,而设计模式通常是我们围绕它们工作的方式。

无论如何,您可能会发现尝试使用“更干净”的FP语言是很有趣的,比如ML(我个人最喜欢的语言,至少在学习的目的上是这样的),或者Haskell,在这些语言中,当您面对新事物时,您没有OOP的拐杖可以依靠。


不出所料,有些人反对我将设计模式定义为“修补语言中的缺陷”,所以我的理由如下:

如前所述,大多数设计模式都特定于一种编程范式,有时甚至是一种特定的语言。通常,它们解决的问题只存在于该范例中(参见FP的单子,或OOP的抽象工厂)。

为什么在FP中不存在抽象工厂模式?因为它试图解决的问题并不存在。

因此,如果OOP语言中存在FP语言中不存在的问题,那么很明显这是OOP语言的缺点。这个问题是可以解决的,但是你的语言不能这样做,而是需要你编写一堆样板代码来解决它。理想情况下,我们希望我们的编程语言能够神奇地解决所有问题。任何仍然存在的问题原则上都是语言的缺陷。;)

其他回答

坚持住。

听到我声称已经取代了设计模式并揭穿了SOLID and DRY,许多人会更加恼怒。我没有人。尽管如此,我还是正确地建模了协作(制造)架构,并在我的网站http://www.powersemantics.com/上在线发布了构建过程的规则以及背后的代码和科学。

My argument is that design patterns attempt to achieve what manufacturing calls "mass customization", a process form in which every step can be reshaped, recomposed and extended. You might think of such processes as uncompiled scripts. I'm not going to repeat my (online) argument here. In short, my mass customization architecture replaces design patterns by achieving that flexibility without any of the messy semantics. I was surprised my model worked so well, but the way programmers write code simply doesn't hold a candle to how manufacturing organizes collaborative work.

制造=每个步骤都与一个产品相互作用 OOP =每个步骤都与自身和其他模块交互,像无用的上班族一样将产品从一点传递到另一点

这种架构永远不需要重构。还有一些关于中心化和分布式的规则会影响复杂性。但为了回答你的问题,函数式编程是另一组处理语义,而不是用于大规模定制流程的体系结构,其中1)源路由作为(脚本)文档存在,使用者可以在触发之前重写,2)模块可以轻松动态地添加或删除。

我们可以说OOP是“硬编码过程”范式,而设计模式是避免这种范式的方法。但这就是大规模定制。设计模式将动态过程体现为混乱的硬代码。没有任何意义。f#允许将函数作为参数传递,这意味着函数式语言和面向对象语言都试图自己完成大规模定制。

代表脚本的硬代码会让读者感到困惑吗?如果你认为你的编译器的消费者为这些特性买单,那就完全不是,但对我来说,这些特性是语义上的浪费。它们是毫无意义的,因为大规模定制的重点是使过程本身是动态的,而不仅仅是对使用Visual Studio的程序员是动态的。

正如公认的答案所说,OOP和FP都有其特定的模式。

然而,有一些模式是非常常见的,我能想到的所有编程平台都应该有。以下是一个(不完整的)列表:

Adapter. I can hardly think of a useful programming platform which is so comprehensive (and self-fulfilled) that it does not need to talk to the world. If it is going to do so, an adapter is definitely needed. Façade. Any programming platforms that can handle big source code should be able to modularise. If you were to create a module for other parts of the program, you will want to hide the "dirty" parts of the code and give it a nice interface. Interpreter. In general, any program is just doing two things: parse input and print output. Mouse inputs need to be parsed, and window widgets need to be printed out. Therefore, having an embedded interpreter gives the program additional power to customise things.

另外,我注意到在典型的FP语言Haskell中,有一些类似于GoF模式的东西,但名称不同。在我看来,这表明它们存在,因为在FP和OOP语言中都有一些共同的问题需要解决。

单子转换器和装饰器。前者用于向现有的单子中添加额外的能力,后者用于向现有的对象中添加额外的能力。

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原则超越了任何语言范式。

甚至OO设计模式解决方案也是特定于语言的。

设计模式是编程语言无法解决的常见问题的解决方案。在Java中,单例模式解决单一的(简化的)问题。

在Scala中,除了Class,还有一个顶级的构造,叫做Object。它是惰性实例化的,只有一个。你不必使用单例模式来获得一个单例。这是语言的一部分。

函数式编程消除了对面向对象设计模式的需求这一说法是否属实?

函数式编程与面向对象编程不同。面向对象的设计模式不适用于函数式编程。取而代之的是函数式编程设计模式。

对于函数式编程,您不需要阅读面向对象设计模式书籍;你会读到其他关于FP设计模式的书籍。

语言无关的

不完全。对于面向对象语言,只有语言不可知。这种设计模式根本不适用于过程式语言。它们在关系数据库设计上下文中几乎没有意义。它们不适用于设计电子表格。

一个典型的OOP设计模式和它的功能对等物?

上述情况不应该存在。这就像要求将一段过程代码重写为OO代码。嗯……如果我将原始的Fortran(或C)翻译成Java,我所做的只是翻译它。如果我完全将其重写为面向对象范式,它将不再看起来像原始的Fortran或C语言——它将无法识别。

从面向对象设计到功能设计没有简单的映射。他们看问题的方式完全不同。

函数式编程(像所有编程风格一样)具有设计模式。关系数据库有设计模式,OO有设计模式,过程式编程有设计模式。任何事物都有设计模式,甚至是建筑物的结构。

设计模式——作为一个概念——是一种永恒的构建方式,与技术或问题领域无关。但是,特定的设计模式适用于特定的问题领域和技术。

每个人只要仔细思考自己在做什么,就会发现设计模式。