函数式编程会取代GoF设计模式吗?


1046

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

我遇到的大多数人都阅读 过《四人帮》(GoF)的《设计模式》一书。任何自以为是的程序员都会告诉您,该书与语言无关,并且无论您使用哪种语言,该模式通常适用于软件工程。这是一个崇高的主张。不幸的是,它与事实相去甚远。

函数式语言极富表现力。在一种功能性语言中,因为该语言可能非常高级,所以不需要设计模式,因此您最终只能使用可以消除所有设计模式的概念进行编程。

函数式编程(FP)的主要功能包括一流的功能,柯里化的,不变的值等。在我看来,OO设计模式无法近似所有这些功能。

此外,在支持OOP的功能语言(例如F#和OCaml)中,对我来说显而易见的是,使用这些语言的程序员将使用与其他OOP语言相同的设计模式。实际上,现在我每天都使用F#和OCaml,并且在这些语言中使用的模式与在用Java编写时使用的模式之间没有显着差异。

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


18
你可能看文章由史蒂夫·耶格(steve-yegge.blogspot.com/2006/03/...
拉尔夫

27
“该书与语言无关,并且模式通常适用于软件工程”-应该指出的是,该书与这种说法不同,因为某些语言不需要表达某些东西,例如设计模式:假设采用Smalltalk / C ++级别的语言功能,这种选择决定了可以轻松实现的内容,例如,CLOS具有多种方法,从而减少了对Visitor(页面331)之类的模式的需求。(第4页)
Guildenstern 2014年

6
还请记住,在足够高级的命令式语言中,甚至不需要许多设计模式。
R. Barzell

3
@ R.Barzell什么是“足够高级的命令式语言”?谢谢。
cibercitizen15年

5
@ cibercitizen1鸭式语言,支持高阶函数和匿名函数。这些功能提供了许多设计模式想要提供的强大功能。
R. Barzell

Answers:


1075

您引用的博客文章夸大了其声明。FP并没有消除对设计模式的需求。术语“设计模式”只是没有被广泛用于在FP语言中描述同一件事。但是它们存在。函数式语言具有许多最佳实践规则,其形式为“遇到问题X时,使用看起来像Y的代码”,这基本上就是一种设计模式。

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

我认为一般地说设计模式只是为了弥补该语言的缺点而引起的争议并不特别有争议。而且,如果另一种语言可以轻松解决相同的问题,那么该另一种语言将不需要设计模式。使用该语言的用户甚至可能没有意识到问题的存在,因为,这不是该语言的问题。

这是“四人帮”对此问题的评价:

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

(以上引自《设计模式简介》第4页第3段)

函数式编程的主要功能包括一流的值,currying,不可变的值等功能。对于我来说,OO设计模式几乎可以近似所有这些功能。

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

因此,是的,由于存在更强大且更易于使用的替代方案,因此几种GoF设计模式在FP语言中变得多余。

但是,当然,仍然有设计这些图案不是通过FP语言解决。FP等于单身人士吗?(暂时忽略单例通常是一个可怕的模式。)

而且它也双向起作用。如我所说,FP也有其设计模式。人们通常通常不会这样认为。

但是您可能遇到过单子。如果不是“应对全球状态”的设计模式,它们是什么?这个问题在OOP语言中是如此简单,以至于那里没有等效的设计模式。

我们不需要一个设计模式为“增加一个静态变量”,或“从套接字读取”,因为它是你正是这样做

说单子是一种设计模式,就像说整数以其通常的操作和零元素是一种设计模式一样荒谬。不,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,请参见monad;有关OOP,请参见抽象工厂)。

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

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


73
设计模式描述了基本问题的一般解决方案。但这也是编程语言和平台的作用。因此,当您使用的语言和平台不够用时,您可以使用设计模式。
yfeldblum

135
S.Lott:是的,他们描述了以给定语言存在的问题的解决方案。FP语言中没有命令设计模式,因为它试图解决的问题不存在。这意味着他们解决了语言本身无法解决的问题。也就是说,在语言的缺点
jalf

38
monad是一个数学概念,您可以通过分类对其进行扩展。当然,您可以将函数,monoids,monads,矩阵或其他数学概念视为设计模式,但它们更像是算法和数据结构...基本概念,独立于语言。
亚历山德鲁·内德尔库

41
当然,单子是一个数学概念,但它们也是一个模式。单子的“ FP模式”与单子的数学概念有些不同。前者是一种模式,用于绕过纯FP语言中的某些“限制”。后者是一个通用的数学概念。
jalf

69
请注意,Haskell中的monad用于可变状态以外的其他用途,例如用于异常,连续性,列表推导,解析,异步编程等。但是所有这些monad的应用都可能称为模式。
2009年

151

函数式编程消除了对OOP设计模式的需求,这有何道理?

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

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

语言不可知

不完全是。面向对象语言仅与语言无关。设计模式根本不适用于过程语言。在关系数据库设计上下文中,它们几乎没有任何意义。在设计电子表格时,它们不适用。

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

以上不应该存在。这就像要求将一部分程序代码重写为OO代码一样。嗯...如果我将原始的Fortran(或C)翻译成Java,除了翻译外,我别无选择。如果我将其完全重写为OO范例,它将不再看起来像原始的Fortran或C一样-将无法识别。

从OO设计到功能设计之间没有简单的映射。他们是解决问题的非常不同的方式。

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

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

每个考虑自己正在做什么的人都会发现设计模式。


12
MVC不是OO设计。这是建筑设计-这种模式适用范围很广。
S.Lott

1
@公主:函数式编程不一定会更简单。在您的示例中,是的。至于其他事情,陪审团还没有成立。但是您已经放弃了Java OO设计模式,而采用了FP设计模式。
S.Lott

1
+1:相对于上述Jalf,我更喜欢这个答案。尽管某些设计模式解决了该语言的缺陷,但并非所有都可以。例如,我几乎不会说“消除递归结”设计模式可以解决语言的缺陷,这只是放松依赖关系的有用习语。
乔恩·哈罗普

9
Java 8将包括闭包(aka匿名函数)或lambda表达式。这将使Java淘汰命令设计模式。这是语言缺陷的一个例子,不是吗?他们添加了一项缺少的功能,现在您不需要设计模式。
Todd Chaffee 2012年

2
+1为结束句。设计模式旨在简化编程,并使最终的程序更有效地实现预期目的。
Sorter

46

布莱恩(Brian)关于语言与模式之间紧密联系的评论很明确,

讨论中缺少的部分是成语的概念。James O. Coplien的书“ Advanced C ++”在这里产生了巨大的影响。早在他发现克里斯托弗·亚历山大(Christopher Alexander)和《无名专栏》(您也无法阅读亚历山大就无法明智地谈论模式)之前,他谈到掌握成语对真正学习语言的重要性。他以C语言中的字符串复制为例,while(*from++ = *to++);您可以将其视为缺少语言功能(或库功能)的创可贴,但真正重要的是,它是比任何一种语言都更大的思想或表达单元它的零件。

这就是模式和语言正在尝试做的事情,以使我们能够更简洁地表达我们的意图。思想单位越丰富,您可以表达的思想就越复杂。从系统体系结构到微妙的变化,在一系列范围内拥有丰富,共享的词汇表,使我们能够进行更明智的对话,并思考应该做什么。

作为个人,我们也可以学习。这是整个练习的重点。我们每个人都可以理解和使用我们永远无法想到的事物。语言,框架,库,模式,习惯用语等在分享知识财富方面都有自己的位置。


8
谢谢!就是模式的意义所在-“概念分块”以降低认知负担。
Randall Schulz,2009年

功能性Monad绝对属于此讨论。
格雷格2012年

@RandallSchulz:语言功能(当然还有它们的惯用用法)也很适合“概念块化以降低认知负担”。
罗伊·廷克

39

GoF书明确地将自己与OOP联系在一起-标题是“设计模式-可重用的面向对象软件的元素” (重点是我的)。



26

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

Edward在其博客文章中以Haskell的形式描述了所有23种原始GoF模式。


4
本文似乎并没有真正显示Haskell中的设计模式,而是展示了Haskell如何在没有上述模式的情况下满足这些需求。
Fresheyeball 2014年

3
@Fresheyball:取决于您对模式的定义。在列表上映射功能是否是访客模式的变体?我一般认为答案是“是”。模式应该超越特定的语法。所应用的函数可以包装为对象,也可以作为函数指针传递,但是对我来说,概念是相同的。你不同意吗?
srm

20

当您尝试从“设计模式”(通常)和“ FP与OOP”的角度进行研究时,您发现的答案充其量将是模糊的。

但是,在两个轴上都更深入,并考虑特定的设计模式特定的语言功能,事情会变得更加清晰。

因此,例如,当使用一种具有代数数据类型和模式匹配闭包一流功能等的语言时,某些特定的模式(例如VisitorStrategyCommandObserver)肯定会更改或消失。GoF书中的其他模式仍然不过,“坚持”。

总的来说,我会说,随着时间的流逝,新的(或只是人气上升)语言功能正在消除特定的模式。这是语言设计的自然过程。随着语言变得更加高级,以前只能在本书中使用示例进行调用的抽象现在已成为特定语言功能或库的应用程序。

(此外:这是我最近写的博客,其中还有其他链接,可用于对FP和设计模式进行更多讨论。)


您如何说访客模式“消失”?难道不是只是从“使用一系列访问方法创建一个访问者接口”变成“使用联合类型和模式匹配”?
加布

22
是的,但是从模式(一种您在书中阅读并应用于代码的设计思想)变为“仅代码”。也就是说,“使用联合类型和模式匹配”通常就是您通常用这种语言编写代码的方式。(类比:如果没有语言有for循环,而所有语言都只有while循环,那么“ For”可能是迭代模式。但是,当for语言仅支持一种结构以及人们如何正常编码时,则不是模式-您不会不需要模式,这只是代码,伙计。)
Brian

4
换句话说,对于“是否是一种模式”来说,一个可能不错的石蕊测试是:以这种方式编写的代码提供给主修CS的二年级本科生,并具有一年的语言编程经验。如果您向他们展示代码,并且他们“设计得很巧妙”,那么这就是一种模式。如果您向他们显示代码,而他们却说“嗯,嗯!”,那不是一个模式。(如果向做过ML / F#/ Haskell一年的任何人展示此“访问者”,他们都会“好吧,well!”)
Brian

1
布莱恩:我认为我们对“模式”的定义不同。我认为任何可识别的设计抽象都是一种模式,而您只将非显而易见的抽象视为一种模式。仅仅因为C#具有foreach和Haskell具有mapM并不意味着它们没有Iterator模式。我说Iterator模式IEnumerable<T>在C#中作为通用接口Traversable在Haskell中作为类型类实现,这没问题。
加布

非显而易见的模式可能适用于软件工程师,但所有模式均适用于语言设计人员。即“如果要创建一种新的语言,请确保包括一种表达迭代器模式的简洁方法。” 当我们开始问“是否有更好的语法来表达这个想法”时,甚至显而易见的模式也会引起人们的兴趣。毕竟,这就是导致有人创建foreach的原因。
srm

16

Norvig的演示暗示他们对所有GoF模式进行了分析,他们说23种模式中的16种在功能性语言中具有更简单的实现,或者只是该语言的一部分。因此,大概其中至少有七个是a)同样复杂,或者b)语言中没有。对于我们来说不幸的是,它们没有被列举出来!

我认为很明显,GoF中的大多数“创造性”或“结构性”模式只是使Java或C ++中的原始类型系统执行所需功能的技巧。但是无论您使用哪种语言,其余的内容都值得考虑。

一种可能是原型;另一种可能是原型。虽然它是JavaScript的基本概念,但必须从头开始以其他语言实现。

我最喜欢的模式之一是“空对象”模式:表示没有某种东西作为对象,它什么也不做。使用功能语言建模可能会更容易。但是,真正的成就是观念的转变。


2
自从GoF模式是专为基于类的OOP语言设计的以来,要做的奇怪的分析。似乎有点像分析管扳手是否适合进行电气工作。
优厚的

@munificent:不是。面向对象提供多态性;函数式编程通常提供多态性。
Marcin

@Marcin OO程序员在多态性方面的含义与功能程序员不同。
AndrewC

@AndrewC我不同意。OO程序员可能认为它们的含义有所不同,但事实并非如此。
Marcin

3
@Marcin以我的经验,OO程序员通常指的是子类型多态性(通常仅使用Object),使用强制转换来实现它或临时多态性(重载等)。当功能程序员说多态时,它们表示参数多态(即适用于任何类型的数据-Int,函数,列表),这可能更像OO的通用编程,而不是OO程序员通常称为多态的任何东西。
AndrewC

15

我想说的是,当您拥有像Lisp这样的语言并支持宏时,便可以构建自己的特定于域的抽象,这些抽象通常比通用的惯用语解决方案要好得多。


我完全迷路了。用抽象来表达……这是什么意思?
tuinstoel

2
您可以构建没有宏的特定于域的抽象(甚至是嵌入式的)。宏只是通过添加自定义语法来使它们漂亮。
乔恩·哈罗普

2
您可以将Lisp视为用于构建编程语言的Legos的集合-它是一种语言,但它也是一种元语言。这意味着对于任何问题域,您都可以自定义设计一种没有明显缺陷的语言。这需要一些练习,而KurtGödel可能会不同意,但是值得与Lisp一起花一些时间来看看它带来了什么(提示,宏)。
格雷格2012年

9

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

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

在Scala中,除了类之外,还有一个称为Object的顶级构造。实例化是懒惰的,只有一个。您不必使用Singleton模式来获得Singleton。这是语言的一部分。


8

模式是解决类似问题的方法,这些问题一遍又一遍,然后得到描述和记录。因此,不,FP不会替换模式。但是,FP可能会创建新的模式,并使某些当前的“最佳实践”模式“过时”。


4
GoP模式是一种解决特定类型的编程语言的局限性的方法。例如,“我想间接访问类,并告诉他们创建对象”->“您不能,但是您可以创建类似于元类的对象,称为工厂”。“我想要多次调度”->“您不能,但是可以实现的迷宫叫做“访问者模式”。等等。如果您不是使用具有特定限制的OOP语言,则所有模式都不有意义。
卡兹(Kaz)2013年

1
我不知道在其他语言中它们中的“没有”是有意义的,但是我会同意其中许多在其他语言中没有意义。Adapter和Bridge似乎拥有更多的多语言可能性,从而减少了访问者的注意力,而对于听众而言则更少。但是,跨语言的模式总是会受到“如何在语言Y中进行语言X的操作”的支持,从而掩盖了语言的自然界限。一个完美的例子是Singleton模式,基本上,该如何在OOP中获得C全局变量?(我会回答,您不应该)。
Edwin Buck 2013年

1
我第二个Kaz:模式不是“解决一遍又一遍的相似问题的方式”,而是“解决一遍又一遍的相似问题的方法,并且必须反复重写,因为该语言不允许只写一次”。换句话说,如果语言允许在库/类/模块等中提取/提取模式,则它不再是一种模式,而是成为库/类/模块。在FP中,将代码的摘要提取/提取为函数要容易得多,因此,“模式”在可重用的代码中更容易转换,从而使其不成为模式。
mb14 2014年

1
欢迎您的解释,但是GoF书很清楚地定义了一种模式,如果您阅读介绍性的章节,那么它对语言或语言的弱点一无所知。当然,某些语言在某些领域会使其更频繁地利用某些模式,但是无论您编写十次(剪切和粘贴)还是使用十种实现一次实现(子类化),还是配置为略微实现十种的框架以不同的方式公开的只是模式的实现细节。
Edwin Buck

1
几年后,我回到了这次对话中,许多人将模式与特定的编程语言或特定的编程范例相关联。可以在这样的上下文中使用它们,但是它们在编程之前就已经存在。“永恒的建筑方式”讨论了建筑架构和社区规划中的模式。这意味着面向模式的技术可以在“语言的局限性”之外使用,除非您要将建筑称为编程语言:)
Edwin Buck

8

就像其他人所说的那样,有一些特定于函数式编程的模式。我认为摆脱设计模式的问题不仅仅是切换到功能性问题,而是语言功能问题

看一下Scala如何消除“单一模式”:您只需声明一个对象而不是一个类。模式匹配的另一个功能有助于避免访问者模式的笨拙。在这里查看比较: Scala的模式匹配=类固醇上的访客模式

和F#一样,Scala是OO功能的融合。我不了解F#,但它可能具有这些功能。

闭包以功能语言显示,但不必局限于此。它们有助于委托人模式。

再观察一次。这段代码实现了一个模式:这是一个经典模式,它是如此的基本,以至于我们通常不将其视为“模式”,但是可以肯定的是:

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

诸如Java和C#的命令式语言已经采用了实质上是一种功能结构来处理此问题:“ foreach”。


我会说Scala为单例模式提供了一流的支持。模式仍然存在,但是与Java相比,应用该模式所需的样板代码大大减少了。
2009年

如果意见像********,那么……请看其余的答案。“您只声明一个对象而不是一个类”确实如此,尽管如此,我还是将其显式地称为对象文字(即var singleton = {};)。我也喜欢提到foreach模式。不幸的是,似乎大多数回答/评论这个问题的人都不了解函数式编程,而宁愿使用OOP设计模式。+1提供具体示例,如果可以的话,我会提供更多。
埃文·普赖斯

@JacquesB我无法对Scala / Haskell进行评论,但是在JavaScript(即混合功能/命令式)中,绝对没有样板,您只是通过使用对象文字语法,匿名函数,首先传递函数的组合来调整声明对象的方式类成员,并允许多重继承(消除了对接口协定的需要)。
埃文·普赖斯

8

GoF 设计模式正在为作为Simula 67后代的OO语言编写变通方案如Java和C ++)的。

设计模式处理的大多数“弊端”是由以下原因引起的:

  • 静态类型的类,它们指定对象,但本身不是对象;
  • 对单调度的限制(仅使用最左边的参数来选择方法,其余的参数仅被视为静态类型:如果它们具有动态类型,则取决于该方法使用临时方法进行分类);
  • 常规函数调用与面向对象函数调用之间的区别,这意味着面向对象函数不能作为期望常规函数的函数参数传递,反之亦然;和
  • “基本类型”和“类类型”之间的区别。

尽管解决方案的结构与相应的设计模式基本相同,但在通用Lisp对象系统中,没有哪一种设计模式不会消失。(此外,该对象系统比GoF本书早十年了。CommonLisp在该书首次发布的同一年成为ANSI标准。)

就功能编程而言,模式是否适用于它取决于给定的功能编程语言是否具有某种对象系统,以及是否根据受益于这些模式的对象系统进行建模。这种类型的面向对象不能与功能编程很好地融合在一起,因为状态的突变位于前端和中间。

构造和非变异访问与功能编程兼容,因此与抽象访问或构造有关的模式可能适用:诸如Factory,Facade,Proxy,Decorator和Visitor等模式。

另一方面,状态和策略之类的行为模式可能不会直接应用于功能性OOP,因为状态突变是其核心。这并不意味着它们不适用;也许它们以某种方式与可用于模拟可变状态的任何技巧结合使用。


2
“ GoF设计模式正在编码解决方法配方”只是一个错误的说法。
约翰·彼得斯

7

我想插入杰里米·吉本斯(Jeremy Gibbons)撰写的几篇优秀的论文,但它们有些密集:“将模式设计为​​高阶数据类型通用程序”和“迭代器模式的本质”(均可在此处访问:http:// www。 comlab.ox.ac.uk/jeremy.gibbons/publications/)。

这些都描述了惯用的功能构造如何覆盖其他(面向对象)设置中特定设计模式所覆盖的地形。


6

不提出类型系统就无法进行讨论。

函数式编程的主要功能包括一流的值,currying,不可变的值等功能。对于我来说,OO设计模式几乎可以近似所有这些功能。

这是因为这些功能无法解决OOP所面临的相同问题……它们是命令式编程的替代方法。FP对OOP的答案在于ML和Haskell的类型系统,特别是求和类型,抽象数据类型,ML模块和Haskell类型类。

但是,当然还有FP语言无法解决的设计模式。FP等于单身人士吗?(暂时忽略单例通常是一个可怕的模式)

类型类要做的第一件事是消除对单例的需求。

您可以查看清单23并消除更多,但我现在没有时间。


6
类型类(相当于OOP接口的FP)如何消除对单例的需求(相当于全局状态的FP)?
加布

4

我认为只有两个GoF设计模式可以将功能编程逻辑引入自然的OO语言。我考虑战略和指挥。可以通过功能编程来修改其他一些GoF设计模式,以简化设计并保持目的。


4
事实是,许多模式的主要目的是利用多态来做对FP概念的体面支持可以自动允许的事情。(例如,我见过的大多数关于Builder的化身只是半成品。)一旦您可以轻松地将函数视为值,模式通常会简化为琐碎的事情。它们变成“传递回调”或“具有回调字典”,例如,不同的构建器类几乎消失了。IMO模式一旦变得微不足道就足以说明事物如何工作的,而不是您需要实现的事物,它就不再是一种模式。
cHao 2013年


3

函数式编程不能替代设计模式。设计模式无法替换。

模式只是存在;他们随着时间的流逝而出现。GoF书将其中一些正式化。如果随着开发人员使用功能性编程语言而出现新的模式,那将是令人兴奋的事情,也许还会有关于它们的书。


1
设计模式不能替代吗?我认为这有点封闭。我们可能都同意设计模式旨在解决编程问题,并且我至少希望希望有一天我们能够在没有设计模式的情况下解决这些问题。
大都会

3
任何特定的模式都可以替换,但是模式的概念不能替换。请记住,术语“模式”出现在建筑领域。
Frank Shearar 2011年

1
模式并不意味着解决编程问题。模式是我们编程的方式。模式文档旨在帮助解决编程问题。
Torbjørn

3
@Torbjørn:模式是当语言妨碍我们编程的方式。之所以存在它们,是因为程序的期望行为与语言的内置功能之间存在某些不匹配,其中要求和功能之间的映射不佳或映射不明确。如果不是那样的话,那就没有模式了。您将拥有一个完成事情的实现,而其他实现实际上将不值得考虑。
cHao 2013年

1
除了真正存在的模式只是为了促进沟通。没有其他目的。多年来,在我参加的所有设计会议中,对算法的讨论最重要而不是模式。这种模式很少以任何有意义的意义解释真正发生的事情。它能准确解释O(n)与O(n Log(n))的影响吗?否。它说明了它与现有架构的适应程度如何?不可以。全尺寸算法的讨论可以。我并不是在说模式本身应该退役,但是如果是的话,几乎没有任何后果。

3

在2013年出版的新书《 Scala和Clojure中的功能编程模式》中的,作者Michael.B。Linn在很多情况下比较了GoF模式并提供了替代品,并做了不错的工作,还讨论了诸如“尾递归”,“记忆化”,“惰性序列”等较新的功能模式。

这本书可在亚马逊上找到。当我经历了几十年的面向对象的背景时,我发现它非常有启发性并且令人鼓舞。


3

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

由于在实际函数编程中不存在任何状态,因此应用GoF模式没有任何意义。功能设计模式与GoF设计模式不同。与现实相反,每种功能设计模式都是人为的,因为功能是数学的构造而不是现实。

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

功能语言的兴起仅是因为一件事:当前物理上的自然限制。由于物理定律,当今的处理器处理指令的速度受到限制。您会看到时钟频率停滞,但处理内核有所扩展。这就是为什么指令并行性对于提高现代应用程序的速度变得越来越重要的原因。根据定义,由于函数式编程没有状态,因此没有副作用,因此可以安全地并行处理函数。

GoF模式不是过时的。它们至少是模拟现实世界需求所必需的。但是,如果使用功能性编程语言,则必须将它们转换为它们的等效混合语言。最后,如果您使用持久性,则没有机会仅制作功能程序。对于程序的混合元素,仍然需要使用GoF模式。对于任何其他纯功能性元素,由于没有状态,因此无需使用GoF模式。

由于GoF模式对于实际的函数式编程而言不是必需的,因此这并不意味着不应使用SOLID原理。SOLID原则超越了任何语言范式。


2
FP可以具有状态-只有全局,共享或可变状态。
vt5491

2

在函数式编程中,设计模式具有不同的含义。实际上,由于更高级别的抽象和用作构造块的HOF,大多数OOP设计模式在函数式编程中都是不必要的。

HOF的原理意味着可以将函数作为参数传递给其他函数。函数可以返回值。


1

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

但是,有些模式是如此普遍,以至于我能想到的所有编程平台都应该具备这种模式。这是一个(不完整的)列表:

  • 适配器。我几乎想不出一个有用的编程平台,它是如此全面(并且可以自我实现),因此不需要与世界交流。如果要这样做,则肯定需要适配器。

  • 正面。任何可以处理大型源代码的编程平台都应该能够模块化。如果要为程序的其他部分创建模块,则需要隐藏代码的“脏”部分并为其提供一个漂亮的界面。

  • 口译员 通常,任何程序都只是在做两件事:解析输入和打印输出。鼠标输入需要解析,并且窗口小部件需要打印出来。因此,拥有嵌入式解释器可以使程序具有自定义内容的附加功能。

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

  • Monad变压器和装饰器。前者用于向现有的monad添加附加功能,后者用于向现有对象添加附加功能。

1

我认为每种范式都有不同的用途,因此无法以这种方式进行比较。

我还没有听说过GoF设计模式适用于每种语言。我听说它们适用于所有OOP语言。如果使用函数式编程,则要解决的问题的领域与OO语言不同。

我不会使用功能性语言来编写用户界面,但是像C#或Java这样的OO语言之一将使这项工作变得更加容易。如果我正在编写一种功能语言,那么我将不考虑使用OO设计模式。


1

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

但是,这两个范例不一定是100%矛盾的,可以一起应用以从两个领域中受益。

即使使用不支持C#之类的本机语言的语言,如果您了解FP原理,也可以编写功能代码。同样,如果您了解OOP原则,模式和最佳实践,则可以使用F#来应用OOP原则。无论使用哪种编程语言,都将根据要解决的情况和问题做出正确的选择。


1

使用支持FP的语言更容易实现某些模式。例如,可以使用闭包很好地实现策略。但是,根据上下文,您可能更喜欢使用基于类的方法来实现策略,例如,策略本身非常复杂和/或要使用模板方法建模的共享结构。

根据我在多范例语言(Ruby)中的开发经验,FP实现在简单的情况下效果很好,但是在上下文更复杂的情况下,基于GoF OOP的方法更合适。

FP方法不能替代OOP方法,而是可以对其进行补充。


0

恕我直言,函数式编程的最重要特征是您只能使用表达式进行编程 -表达式中的表达式内部的表达式全部求值到最后一个最终表达式,“最终值会在评估时使机器发热”。

恕我直言,面向对象编程的最重要特征是您正在使用具有内部状态的对象进行编程。您不能在纯函数中具有内部状态-面向对象的编程语言需要声明来使事情发生。(在函数式编程中没有语句。)

您正在将苹果与橙子进行比较。面向对象编程的模式不适用于函数编程,因为函数编程是使用表达式进行编程,而面向对象编程是使用内部状态进行编程。


嗯,我应该已经注意到问题在回答之前已经有11年了。:-)
Jim Flood

0

支撑自己。

听到我声称已经更换了设计模式并拆穿了SOLID和DRY,这将使许多人感到不安。我谁都不是。不过,我正确地建模了协作(制造)体系结构,并在我的网站http://www.powersemantics.com/上发布了在线构建流程的规则以及其背后的代码和科学知识

我的观点是,设计模式试图实现制造所谓的“批量定制”,即一种可以重塑,重组和扩展每个步骤的过程形式。您可能会将此类过程视为未编译的脚本。我不会在这里重复我的(在线)论点。简而言之,我的大规模定制体系结构通过在没有任何混乱语义的情况下实现了这种灵活性来替代了设计模式。我的模型运行良好,令我感到惊讶,但是程序员编写代码的方式根本无法理解制造业如何组织协作工作。

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

这种架构永远不需要重构。对于集中化和分布,还有一些规则会影响复杂性。但是要回答您的问题,函数式编程是另一组处理语义,而不是用于大规模定制过程的体系结构,其中,1)源路由以(script)文档的形式存在,wielder可以在触发前重写它们,以及2)模块可以轻松地实现,动态添加或删除。

我们可以说OOP是“硬编码过程”范式,而设计模式是避免这种范式的方法。但这就是大规模定制的全部内容。设计模式将动态过程体现为凌乱的硬编码。毫无意义。F#允许将函数作为参数传递的事实意味着功能和OOP 语言都试图自行完成大规模定制。

代表脚本的硬编码对读者来说有多混乱?如果您认为编译器的使用者为此类功能付费,那根本就不是,但对我而言,此类功能是语义上的浪费。它们毫无意义,因为大规模定制的目的是使流程本身具有动态性,而不仅仅是使使用Visual Studio的程序员具有动态性。


0

这样做确实是因为高级功能性PL(例如OCaml,带有类,模块等)在类型的多功能性和表达能力方面肯定会取代OOP命令性语言。抽象不会泄漏,您可以在程序中直接表达大多数想法。因此,是的,它确实替代了设计模式,无论如何,大多数设计模式与功能模式相比都极其简单。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.