博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
函数式编程会取代GoF设计模式吗?
阅读量:3575 次
发布时间:2019-05-20

本文共 8325 字,大约阅读时间需要 27 分钟。

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

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

函数式语言极富表现力。 在一种功能性语言中,不需要设计模式是因为该语言可能太高级了,您最终会在概念上进行编程,从而一起消除了所有设计模式。

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

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

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


#1楼

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


#2楼

这是讨论此主题的另一个链接: :

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


#3楼

本质上

  • 当模式绕过缺失的特征(高阶函数,流处理...)时, 促进 。
  • 一次又一次地重写模式实现的需求本身可以被视为一种 。

此外,此提供了“模式/功能”转换表以及一些不错的讨论(如果您愿意的话)。


#4楼

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


#5楼

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


#6楼

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

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

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

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

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

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

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


#7楼

在2013年新出版的名为“ Functional Programming Patterns- in Scala and Clojure”的书中,作者Michael.B。 Linn在很多情况下对GoF模式进行比较和提供替代品方面做得不错,还讨论了诸如“尾递归”,“记忆化”,“惰性序列”等较新的功能模式。

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


#8楼

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

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

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

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

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

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

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

#9楼

函数式编程消除了对OOP设计模式的需求,这有什么道理可言吗?

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

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

语言不可知

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

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

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

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

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

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

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


#10楼

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

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

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


#11楼

Peter Norvig的的对这个通用主题进行了周到的介绍,尽管它涉及的是“动态”语言而不是“功能”语言(存在重叠)。


#12楼

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

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

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

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

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

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

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


#13楼

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

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

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

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

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

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

函数式编程的主要特征包括函数,例如一流的值,currying,不可变的值等。在我看来,OO设计模式无法近似所有这些功能。

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

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

但是,当然,仍然有设计通过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语言很有趣,例如 (我个人最喜欢的,至少出于学习目的)或 ,当您没有OOP拐杖时面临着新的事物。


不出所料,一些人反对我对设计模式的定义为“弥补一种语言的缺陷”,所以这是我的理由:

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

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

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


#14楼

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

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

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

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

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


#15楼

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

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

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

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


#16楼

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

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

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


#17楼

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

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


#18楼

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

函数式编程的主要特征包括函数,例如一流的值,currying,不可变的值等。在我看来,OO设计模式无法近似所有这些功能。

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

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

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

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


#19楼

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


#20楼

我想插入杰里米·吉本斯(Jeremy Gibbons)撰写的几篇出色的论文,但论文有些密集:“将模式设计为​​高阶数据类型通用程序”和“迭代器模式的本质”(均可在此处访问: )。

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


#21楼

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

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

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

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

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

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


#22楼

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

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

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

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


#23楼

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

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

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


#24楼

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

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

转载地址:http://qxogj.baihongyu.com/

你可能感兴趣的文章
git本地仓库和远程仓库关联,分支重命名
查看>>
js对象的深拷贝,你真的觉得很简单吗?
查看>>
你真的了解map方法吗?手动实现数组map方法。
查看>>
带你手动实现call方法,让你收获满满
查看>>
前端知识体系
查看>>
使用join查询方式找出没有分类的电影id以及名称
查看>>
Qt教程(2) : Qt元对象系统
查看>>
驱动开发误用指针错误:Unable to handle kernel NULL pointer dereference at virtual address
查看>>
Linux部署DocSystem知识/文件管理系统
查看>>
Centos7开机自启动脚本无法使用备用方案
查看>>
jvm虚拟机内存详解
查看>>
线程的创建方式
查看>>
DNS是什么
查看>>
Hbase架构
查看>>
PaddleX的C++使用
查看>>
MyBatis-Plus代码生成器
查看>>
我的第一个SpringBoot项目(一)
查看>>
回文数
查看>>
伪背包问题!
查看>>
求10000以内n的阶乘!
查看>>