协慌网

登录 贡献 社区

功能编程是否取代了 GoF 设计模式?

自从我去年开始学习 F#和 OCaml 以来,我已经阅读了大量的文章,这些文章坚持认为设计模式(特别是 Java)是命令式语言中缺少的功能的变通方法。我发现的一篇文章提出了相当强烈的主张

我遇到过的大多数人都读过 Gang of Four 的 “设计模式” 一书。任何自尊的程序员都会告诉你,这本书与语言无关,而且无论你使用哪种语言,这些模式都适用于软件工程。这是一个崇高的主张。不幸的是,它与事实相去甚远。

功能语言极具表现力。 在函数式语言中,人们不需要设计模式,因为语言可能是如此高级,您最终会编写概念,一起消除设计模式。

函数式编程的主要特性包括作为一等值,currying,不可变值等的函数。对我来说,OO 设计模式近似于任何这些特性似乎并不明显。

另外,在支持 OOP 的函数式语言(例如 F#和 OCaml)中,对我来说很明显,使用这些语言的程序员将使用与其他 OOP 语言相同的设计模式。事实上,现在我每天都使用 F#和 OCaml,我在这些语言中使用的模式与我在 Java 中编写时使用的模式之间没有明显的差异。

功能编程是否消除了对 OOP 设计模式的需求,是否有任何理由?如果是这样,您是否可以发布或链接到典型 OOP 设计模式及其功能等效的示例?

答案

你引用的博客文章夸大了它的主张。 FP 不会消除对设计模式的需求。术语 “设计模式” 并未广泛用于描述 FP 语言中的相同内容。但他们存在。函数式语言有很多最佳实践规则,“当遇到问题 X 时,使用看起来像 Y 的代码”,这基本上就是设计模式。

但是,大多数特定于 OOP 的设计模式在函数式语言中几乎无关紧要是正确的。

我认为一般来说 ,设计模式只能用于弥补语言中的缺点,我认为应该特别具有争议性。如果另一种语言可以解决同样的问题,那么其他语言也不需要设计模式。该语言的用户可能甚至不知道问题存在 ,因为,这不是该语言的问题。

以下是四人帮对此问题的看法:

编程语言的选择很重要,因为它会影响一个人的观点。我们的模式假设具有 Smalltalk / C ++ 级语言功能,并且该选择决定了哪些可以轻松实现,哪些不能轻松实现。如果我们假设过程语言,我们可能会包含称为 “继承”,“封装” 和 “多态” 的设计模式。同样,我们的一些模式直接由不太常见的面向对象语言支持。例如,CLOS 有多种方法,可以减少对访问者等模式的需求。事实上,Smalltalk 和 C ++ 之间存在足够的差异意味着某些模式可以在一种语言中比另一种语言更容易表达。 (例如,参见 Iterator。)

(以上是 “设计模式导论” 一书中的引用,第 4 页,第 3 段)

函数式编程的主要特性包括作为一等值,currying,不可变值等的函数。对我来说,OO 设计模式近似于任何这些特性似乎并不明显。

什么是命令模式,如果不是近似的一流函数? :) 在 FP 语言中,您只需将函数作为参数传递给另一个函数。在 OOP 语言中,您必须将该函数包装在一个类中,您可以实例化该函数,然后将该对象传递给另一个函数。效果是一样的,但在 OOP 中它被称为设计模式,它需要更多的代码。什么是抽象工厂模式,如果不是 currying?将参数一次传递给函数,以配置最终调用它时吐出的值。

所以,有些 GoF 设计模式在 FP 语言中变得多余,因为存在更强大和更易于使用的替代方案。

但是,当然,还有设计通过 FP 语言解决模式。单身的 FP 等价物是多少? (暂时忽略单身人士通常使用的可怕模式)

它也有两种方式。正如我所说,FP 也有其设计模式,人们通常不会这样认为它们。

但是你可能遇到过 monad。如果不是 “处理全球国家” 的设计模式,它们是什么?这是一个在 OOP 语言中如此简单的问题,那里不存在等效的设计模式。

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

说 Monad 是一种设计模式,就像说整数与他们通常的操作和零元素是一种设计模式一样荒谬。不,Monad 是一种数学模式 ,而不是一种设计模式。

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

另外,在支持 OOP 的函数式语言(例如 F#和 OCaml)中,对我来说很明显,使用这些语言的程序员将使用与其他 OOP 语言相同的设计模式。事实上,现在我每天都使用 F#和 OCaml,并且我在这些语言中使用的模式与我在 Java 中编写时使用的模式之间没有明显的差异。

也许是因为你还在考虑当务之急?很多人在一生中处理命令式语言后,在尝试使用函数式语言时很难放弃这种习惯。 (我在 F#上看到了一些非常有趣的尝试,其中字面上每个函数只是一串'let' 语句,基本上就像你采用了 C 程序一样,并用'let' 替换了所有的分号。:))

但另一种可能性可能是你没有意识到你正在解决一些需要用 OOP 语言设计模式的问题。

当您使用 currying,或将函数作为参数传递给另一个时,请停下来思考如何使用 OOP 语言执行此操作。

功能编程是否消除了对 OOP 设计模式的需求,是否有任何理由?

是的。 :) 当您使用 FP 语言时,您不再需要特定于 OOP 的设计模式。但是你仍然需要一些通用的设计模式,比如 MVC 或其他非 OOP 特定的东西,你需要一些新的 FP 特定的 “设计模式”。所有语言都有它们的缺点,设计模式通常是我们解决它们的方式。

无论如何,你可能会觉得尝试使用 “更干净” 的 FP 语言很有意思,比如 ML(我个人最喜欢的,至少是出于学习目的),或者 Haskell,当你没有 OOP 拐杖时面对新事物。


正如所料,有些人反对我将设计模式定义为 “修补某种语言中的缺点”,所以这就是我的理由:正如已经说过的,大多数设计模式都是针对一种编程范式,有时甚至是一种特定语言。通常,它们解决仅存在于该范例中的问题(参见 FP 的 monads 或 OOP 的抽象工厂)。为什么 FP 中不存在抽象工厂模式?因为它试图解决的问题不存在。因此,如果在 FP 语言中不存在 OOP 语言中存在问题,那么显然这是 OOP 语言的缺点。问题可以解决,但是你的语言没有这样做,但是需要一堆样板代码来解决它。理想情况下,我们希望我们的编程语言能够神奇地解决所有问题。任何仍然存在的问题原则上都是语言的缺点。 ;)

功能编程是否消除了对 OOP 设计模式的需求,是否有任何理由?

函数式编程与面向对象编程不同。面向对象的设计模式不适用于函数式编程。相反,您拥有函数式编程设计模式。

对于函数式编程,您不会阅读 OO 设计模式书籍,您将阅读有关 FP 设计模式的其他书籍。

语言不可知论者

不是完全的。只有语言与 OO 语言无关。设计模式根本不适用于程序语言。它们在关系数据库设计上下文中几乎没有意义。它们在设计电子表格时不适用。

典型的 OOP 设计模式及其功能等同?

以上不应该存在。这就像要求将一段程序代码重写为 OO 代码。嗯... 如果我将原始的 Fortran(或 C)翻译成 Java,我除了翻译之外没有做任何其他事情。如果我完全将其重写为 OO 范例,它将不再像原始的 Fortran 或 C 那样 - 它将无法识别。

从 OO 设计到功能设计没有简单的映射。他们看待问题的方式截然不同。

函数式编程(与所有编程风格一样)具有设计模式。关系数据库有设计模式,OO 有设计模式,过程编程有设计模式。一切都有设计模式,甚至建筑物的建筑。

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

每个想到他们正在做的事情的人都会发现设计模式。

布莱恩关于语言和模式之间紧密联系的评论是关键所在,

这个讨论的缺失部分是习语的概念。 Coplien 的书 “高级 C ++” 在这里产生了巨大的影响。早在他发现克里斯托弗亚历山大和没有名字专栏之前(你也不能在不读亚历山大的情况下明智地谈论模式),他谈到了掌握成语在真正学习语言中的重要性。他以 C 中的字符串副本为例,(* from ++ = * to ++); 您可以将其视为缺失语言特征(或库特征)的绑定,但真正重要的是它比任何部分都更重要的思想或表达单位。

这就是模式和语言试图做的事情,让我们更简洁地表达我们的意图。思想单元越丰富,你表达的思想就越复杂。拥有丰富的,共享的词汇表,从一系列的规模 - 从系统架构到钻头 - 让我们能够进行更加智能的对话,以及对我们应该做什么的想法。

作为个人,我们也可以学习。这是练习的全部内容。我们每个人都能理解和使用我们永远无法想到的事情。语言,框架,图书馆,模式,习语等都在分享知识财富方面占有一席之地。