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


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

我见过的大多数人都读过   设计模式由Gang of   四。任何自尊的程序员   会告诉你这本书是   语言不可知和模式   适用于软件工程   一般,不管是哪种语言   你用。这是一个崇高的主张。   不幸的是它远离了   真相。

功能语言极其重要   表现力。 用函数式语言   一个人不需要设计模式   因为语言可能很高   等级,你最终编程   消除设计的概念   各种模式。

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

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

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


943
2017-11-29 20:08


起源


你可以看一下Steve Yegge的文章(steve-yegge.blogspot.com/2006/03/...) - Ralph
“这本书与语言无关,这些模式一般适用于软件工程” - 应该指出的是,本书不同意这种说法,因为某些语言不需要表达某些设计模式:“我们的模式假设具有Smalltalk / C ++级别的语言特性,并且该选择决定了可以和不能轻松实现的内容[...] CLOS具有多种方法,例如,它减少了对诸如访问者(页331)之类的模式的需求。 (第4页) - Guildenstern
还要记住,在足够高级的命令式语言中甚至不需要许多设计模式。 - R. Barzell
@ R.Barzell那些“足够高级别的命令式语言”是什么?谢谢。 - cibercitizen1
@ cibercitizen1鸭式语言,支持高阶函数和匿名函数。这些功能提供了许多设计模式所要提供的大部分功能。 - R. Barzell


答案:


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

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

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

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

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

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

功能的主要特征   编程包括函数   一流的价值观,currying,   它是不可变的值等等   OO设计模式对我来说很明显   正在接近其中任何一个   特征。

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

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

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

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

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

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

在(纯)函数语言中,副作用和可变状态是不可能的,除非你使用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语言的缺点。这个问题可以解决,但是你的语言没有这样做,但需要一大堆样板代码来解决它。理想情况下,我们希望我们的编程语言能够神奇地制作 所有 问题消失了。任何仍然存在的问题原则上都是语言的缺点。 ;)


952
2017-11-29 23:06



设计模式描述了基本问题的一般解决方案但这也是编程语言和平台的作用。因此,当您使用的语言和平台不够时,您可以使用设计模式。 - yfeldblum
S.Lott:他们描述了给定语言中存在的问题的解决方案,是的。 FP语言中没有Command设计模式,因为它试图解决的问题不存在。这意味着他们解决了语言本身无法解决的问题。也就是说,语言的缺点 - jalf
monad是一个数学概念,你用你的分类来扩展它。当然,您可以将函数,幺半群,monad,矩阵或其他数学概念视为设计模式,但这些更像是算法和数据结构......基本概念,语言无关。 - Alexandru Nedelcu
当然,monad是一个数学概念,但它们是 也 一种模式。 monad的“FP模式”与monad的数学概念有些不同。前者是用于绕过纯FP语言中某些“限制”的模式。后者是一种通用的数学概念。 - jalf
请注意,Haskell中的monad用于除可变状态之外的其他内容,例如异常,continuation,列表推导,解析,异步编程等。但是monad的所有这些应用都可能被称为模式。 - JacquesB


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

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

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

语言不可知论者

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

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

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

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

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

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

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


135
2017-11-29 20:15



这几乎是我的预期,尽管我认为函数式编程可以简化现有模式的说法有些道理。例如,我发现当我可以将函数作为值传递时,我不再需要传递单方法接口。 - Juliet
MVC不是OO设计。它的架构设计 - 这种模式适用范围非常广泛。 - S.Lott
@Princess:函数式编程不一定比较简单。在你的例子中,是的。对于其他事情,陪审团仍然没有。但是您已经放弃了Java OO设计模式并采用了FP设计模式。 - S.Lott
Java 8将包括闭包,即匿名函数,即lambda表达式。这将使Java的命令设计模式过时。这是语言缺点的一个例子,不是吗?他们添加了一个缺失的功能,现在您不需要设计模式。 - Todd Chaffee
结束语的+1。设计模式旨在简化编程,使得生成的程序更有效,符合他们的目的。 - Sorter


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

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

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

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


40
2018-01-01 20:59



谢谢! 这个 是什么模式是关于 - 降低认知负担的“概念性分块”。 - Randall Schulz
功能Monads肯定属于这个讨论。 - Greg
@RandallSchulz:语言特征(当然还有它们的惯用语)也很适合“降低认知负担的概念性组合”。 - Roy Tinker


GOF书明确地与OOP联系在一起 - 标题是设计模式 - 可重用元素 面向对象 软件(强调我的。)


35
2017-11-24 15:49





动态规划中的设计模式 虽然关于“动态”语言而不是“功能性”(有重叠),但Peter Norvig对这个一般主题进行了深思熟虑的报道。


32
2017-11-29 20:23





这是另一个链接,讨论这个主题: http://blog.ezyang.com/2010/05/design-patterns-in-haskel/

在他的博客文章中,爱德华用Haskell描述了所有23种原始GoF模式。


24
2017-10-19 08:15



这篇文章似乎并没有真正展示Haskell中的设计模式,而是展示了Haskell如何在没有所述模式的情况下满足这些需求。 - Fresheyeball
@Fresheyball:取决于你对模式的定义。是否将列表上的函数映射为访问者模式的变体?我一般认为答案是肯定的。模式应该超越特定的语法。应用的函数可以包装为对象或作为函数指针传递,但对我来说概念是相同的。你不同意吗? - srm


当你试图在“设计模式”(一般)和“FP与OOP”的水平上看时,你会发现的答案充其量是模糊的。

但是,在两个轴上更深一些,并考虑一下 具体的设计模式 和 特定语言功能 事情变得更加清晰。

所以,例如,一些特定的模式,如 游客战略命令,和 观察 使用语言时肯定会改变或消失 代数数据类型和模式匹配关闭一流的功能但是,GoF书中的其他一些模式仍然“坚持”。

总的来说,我会说,随着时间的推移,特定的模式正在被新的(或刚刚升级的)语言特征所消除。这是语言设计的自然过程;随着语言变得更高级,以前只能在书中使用示例调用的抽象现在成为特定语言特征或库的应用程序。

(旁白:这是一个 最近的博客 我写了,其他链接有关FP和设计模式的更多讨论。)


17
2017-11-29 23:15



你怎么能说访客模式“消失”?它是不是只是从“使用一堆访问方法制作一个访问者界面”变成“使用联合类型和模式匹配”? - Gabe
是的,但是从一个改变了 模式 这是一本您在书中阅读并适用于您的代码的设计理念,即“只是编码”。也就是说,“使用联合类型和模式匹配”就是你通常用这种语言编写代码的方式。 (打个比方:如果没有语言的话 for 循环,他们都只是 while 循环,然后“For”可能是迭代模式。但当 for 只是语言支持的构造以及人们如何正常编码,那么它不是一个模式 - 你不需要一个模式,它只是代码,man。) - Brian
换句话说,对于“这是一种模式”而言,一个也许并不差的试金石是:现在的代码以这种方式写给一位二年级本科生,主修CS并且有一年的语言编程经验。如果你向他们展示代码,他们就会“那是一个聪明的设计”,那么这就是一种模式。如果你向他们展示代码,他们会“好吧,呃!”,那么这不是一种模式。 (如果你向任何已经完成ML / F#/ Haskell一年的人展示这个“访客”,他们会“好吧,呃!”) - Brian
布莱恩:我认为我们对“模式”有不同的定义。我认为任何可识别的设计抽象都是一个 模式,虽然你只考虑非显而易见的抽象 模式。只因为C#有 foreach 和哈斯克尔有 mapM 并不意味着他们没有Iterator模式。我认为Iterator模式是作为通用接口实现的,没有问题 IEnumerable<T> 在C#和类型类中 Traversable 在哈斯克尔。 - Gabe
可能是非显而易见的模式对软件工程师有用,但所有模式都对语言设计者有用。即“如果你正在创建一种新语言,请确保包含一种表达迭代器模式的简洁方法。”当我们开始提出问题时,即使是明显的模式也是有意义的,“是否有更好的语法表达这个想法?”毕竟,这是导致某人创造foreach的原因。 - srm