【问题标题】:Does functional programming replace GoF design patterns?函数式编程会取代 GoF 设计模式吗?
【发布时间】:2010-09-24 14:02:58
【问题描述】:

自从我去年开始学习F#OCaml 以来,我已经阅读了大量文章,这些文章坚持认为设计模式(尤其是在Java 中)是命令式语言中缺少的功能的变通方法。我找到的一篇文章makes a fairly strong claim

我遇到的大多数人都读过 the Design Patterns book by 帮派 四(GoF)。任何自尊的程序员 会告诉你这本书是 语言不可知论者和模式 申请软件工程 一般,无论哪种语言 你用。这是一个崇高的主张。 不幸的是,它远离 真相。

函数式语言非常 富有表现力。 使用函数式语言 不需要设计模式 因为语言可能很高 水平,你最终编程在 消除设计的概念 模式都在一起。

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

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

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

【问题讨论】:

  • 你可以看看 Steve Yegge (steve-yegge.blogspot.com/2006/03/…) 的文章
  • “这本书与语言无关,模式通常适用于软件工程” - 应该注意的是,这本书不同意这种说法,因为某些语言不需要表达某些诸如设计模式之类的东西:“我们的模式假设了 Smalltalk/C++ 级别的语言特性,而这种选择决定了哪些可以轻松实现,哪些不能轻松实现 [...] CLOS 有多种方法,例如,这减少了对此类模式的需求作为访客(第 331 页)。” (第 4 页)
  • 另外请记住,许多设计模式在足够高级的命令式语言中甚至不是必需的。
  • @cibercitizen1 鸭型语言,支持高阶函数和匿名函数。这些功能提供了许多设计模式本应提供的大部分功能。

标签: oop design-patterns functional-programming


【解决方案1】:

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

但是,大多数 OOP 特定的设计模式与函数式语言几乎无关,这是正确的。

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

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

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

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

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

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

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

当然,仍然有一些设计模式没有被 FP 语言解决。单例的 FP 等价物是多少? (暂时忽略单例通常是一种糟糕的使用模式。)

它也适用于两种方式。正如我所说,FP 也有它的设计模式。人们通常不这么认为。

但您可能遇到过 monad。如果不是“处理全局状态”的设计模式,它们是什么?这是一个在 OOP 语言中非常简单的问题,以至于不存在等效的设计模式。

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

说 monad 是一种设计模式就像说 Integers 及其通常的操作和零元素是一种设计模式一样荒谬。不,monad 是一种数学模式,而不是设计模式。

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

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

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

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

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

这种说法有什么真实性吗? 函数式编程消除了 需要 OOP 设计模式吗?

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

无论如何,您可能会发现尝试“更干净”的 FP 语言很有趣,例如 ML(我个人最喜欢,至少用于学习目的)或 Haskell,您没有 OOP遇到新事物时可以依靠的拐杖。


正如预期的那样,一些人反对我将设计模式定义为“修补语言中的缺点”,所以这是我的理由:

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

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

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

【讨论】:

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

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

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

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

语言无关

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

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

以上不应该存在。这就像要求将一段程序代码重写为 OO 代码一样。嗯...如果我将原始的 Fortran(或 C)翻译成 Java,我除了翻译它什么也没做。如果我将它完全重写为 OO 范式,它将不再看起来像原来的 Fortran 或 C —— 它将无法识别。

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

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

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

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

【讨论】:

  • MVC 不是 OO 设计。这是建筑设计——这种模式应用得相当广泛。
  • @Princess:函数式编程不一定更简单。在你的例子中,是的。对于其他事情,陪审团仍然没有。但是您已经抛弃了 Java OO 设计模式,而采用了 FP 设计模式。
  • +1:我更喜欢这个答案而不是上面 Jalf 的答案。尽管一些设计模式解决了语言中的缺陷,但并非所有设计模式都能解决。例如,我很难说“解开递归结”设计模式解决了语言的缺陷,它只是放松依赖关系的一个有用的习惯用法。
  • Java 8 将包括闭包,也就是匿名函数,也就是 lambda 表达式。这将使 Java 的命令设计模式过时。这是语言缺陷的一个例子,不是吗?他们添加了一个缺失的功能,现在您不需要设计模式了。
  • +1 表示结束句。设计模式旨在简化编程并使生成的程序更高效,达到预期目的。
【解决方案3】:

Brian 关于语言和模式之间紧密联系的理论非常重要,

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

这就是模式和语言试图做的,让我们更简洁地表达我们的意图。思想单位越丰富,你能表达的思想就越复杂。从系统架构到小游戏,拥有丰富的、共享的词汇表,让我们能够进行更智能的对话,并思考我们应该做什么。

作为个人,我们也可以学习。这是整个练习的重点。我们每个人都可以理解和使用我们永远无法想到的东西。语言、框架、库、模式、习语等都在分享知识财富方面占有一席之地。

【讨论】:

  • 谢谢! 就是模式的意义所在——“概念分块”以降低认知负担。
  • 函数式单子绝对属于这个讨论范围。
  • @RandallSchulz:语言特征(当然还有它们的惯用用法)也很适合“概念分块以降低认知负担”的类别。
【解决方案4】:

GoF 书明确地将自己与 OOP 联系在一起 - 标题是 Design Patterns - Elements of ReusableObject-Oriented 软件(重点是我的)。

【讨论】:

    【解决方案5】:

    @ 987654321由Peter Norvig对此一般主题的周到覆盖范围,但关于“动态”语言而不是“功能”(有重叠)。

    【讨论】:

    • 值得一提的是,只有四种模式被一等函数明确排除。一流的类型最终成为努力的大消除者(消除了六个),但也有同样大量的模式被非常非传统的通用 lisp 对象系统提供的特定功能所消除,该系统实质上概括了 OOP 并使其更多强大的
    【解决方案6】:

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

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

    【讨论】:

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

    当您尝试从“设计模式”(一般)和“FP 与 OOP”的层面来看待这个问题时,您会发现的答案充其量是模糊的。

    不过,在这两个轴上再深入一点,并考虑特定的设计模式特定的语言特性,事情就会变得更加清晰。

    因此,例如,一些特定的模式,例如 VisitorStrategyCommandObserver当使用具有代数数据类型和模式匹配闭包一等函数等的语言时发生变化或消失。GoF 中的一些其他模式不过,这本书仍然“坚持”。

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

    (顺便说一句:这是我写的recent blog,它还有其他链接可以更多地讨论 FP 和设计模式。)

    【讨论】:

    • 你怎么能说访问者模式“消失了”?不就是从“用一堆访问方法做一个访问者接口”变成“使用联合类型和模式匹配”吗?
    • 是的,但是从 pattern(您在书中读到并应用于您的代码的一种设计理念)变成了“只是代码”。也就是说,“使用联合类型和模式匹配”就是您通常用这种语言编写代码的方式。 (类比:如果没有语言有for 循环并且它们都只有while 循环,那么“For”可能是一种迭代模式。但是当for 只是语言支持的构造以及人们如何正常编码时,那么它就不是模式——你不需要模式,它只是代码,伙计。)
    • 换句话说,对于“它是否是一种模式”的一个可能还不错的试金石测试是:将这样编写的代码展示给一个拥有一年编程经验的 CS 专业二年级本科生用你的语言。如果您向他们展示代码,并且他们说“这是一个聪明的设计”,那么它就是一种模式。如果您向他们展示代码,他们会说“嗯,嗯!”,那么这不是一种模式。 (如果你把这个“Visitor”展示给任何做过 ML/F#/Haskell 一年的人,他们会说“好吧,呃!”)
    • Brian:我认为我们只是对“模式”有不同的定义。我认为任何可识别的设计抽象都是模式,而您只认为非显而易见的抽象是模式。仅仅因为 C# 有 foreach 而 Haskell 有 mapM 并不意味着它们没有迭代器模式。我认为迭代器模式在 C# 中实现为通用接口 IEnumerable<T> 和在 Haskell 中实现为类型类 Traversable 没有问题。
    • 可能不明显的模式对软件工程师有用,但所有模式对语言设计者都有用。 IE。 “如果你正在创建一种新语言,请确保包含一种简洁的方式来表达迭代器模式。”当我们开始问“是否有更好的语法来表达这个想法?”时,即使是明显的模式也很有趣。毕竟,这就是导致某人创建 foreach 的原因。
    【解决方案8】:

    我想说,当您拥有像 Lisp 这样支持宏的语言时,您就可以构建自己的特定领域抽象,这些抽象通常比一般的成语解决方案要好得多。

    【讨论】:

    • 我完全迷路了。用抽象来做点什么……这是什么意思?
    • 您可以在没有宏的情况下构建特定领域的抽象(甚至是嵌入式抽象)。宏只是让您通过添加自定义语法来美化它们。
    • 您可以将 Lisp 视为一组用于构建编程语言的乐高积木——它是一种语言,但它也是一种元语言。这意味着对于任何问题域,您都可以定制设计一种没有任何明显缺陷的语言。这需要一些练习,Kurt Gödel 可能不同意,但值得花一些时间与 Lisp 一起看看它带来了什么(提示、宏)。
    • JD:这有点像说你总是可以写汇编。宏可以进行非常重要的代码转换。您可以在技术上用其他语言构建和操作 DSL 的 AST,但是宏可以让您使用您正在使用的语言的正常表达式来做到这一点,以便可以更干净地集成 DSL
    【解决方案9】:

    Norvig 的演讲暗示了他们对所有 GoF 模式所做的分析,他们说 23 种模式中有 16 种在函数式语言中具有更简单的实现,或者只是语言的一部分。所以大概至少有七个是a)同样复杂或b)不存在于语言中。不幸的是,我们没有列举它们!

    我认为很明显,GoF 中的大多数“创建”或“结构”模式只是让 Java 或 C++ 中的原始类型系统执行您想要的操作的技巧。但不管你用什么语言编程,其余的都值得考虑。

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

    我最喜欢的模式之一是 Null Object 模式:将某物的缺失表示为一个不做适当类型的对象。这可能更容易用函数式语言建模。然而,真正的成就是视角的转变。

    【讨论】:

    • GoF 模式是专门为基于类的 OOP 语言设计的,这是多么奇怪的分析啊。似乎有点像分析管钳是否适合做电气工作。
    • @munificent:不是真的。面向对象提供多态性;函数式编程通常提供多态性。
    • @Marcin OO 程序员的多态性与函数式程序员的含义截然不同。
    • @AndrewC 我不同意。 OO 程序员可能认为它们的含义不同,但事实并非如此。
    • @Marcin 以我的经验,OO 程序员通常指的是子类型多态性(通常只使用 Object),利用强制类型转换来实现它,或临时多态性(重载等)。当函数式程序员说多态性时,他们指的是参数多态性(即适用于任何类型的数据 - Int、函数、列表),这可能更像 OO 的泛型编程,而不是像 OO 程序员通常所说的任何东西多态性。
    【解决方案10】:

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

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

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

    【讨论】:

      【解决方案11】:

      模式是解决类似问题的方法,这些问题反复出现,然后得到描述和记录。所以不,FP 不会取代模式。但是,FP 可能会创建新模式,并使一些当前的“最佳实践”模式“过时”。

      【讨论】:

      • GoP 模式是解决特定类型编程语言的限制问题的方法。例如“我想间接使用类,并告诉他们制作对象”->“你不能,但你可以制作类似于元类的对象,称为工厂”。 “我想要多次调度”->“你不能,但是你可以实现一个名为访客模式的迷宫”。等等。如果您使用的不是具有特定限制的 OOP 语言,那么这些模式都没有意义。
      • 我不知道它们在其他语言中“没有”意义,但我同意其中很多在其他语言中没有意义。 Adapter 和 Bridge 似乎有更多的多语言可能性,对于访问者来说减少了一点,对于听众来说可能少了一点。然而,跨语言的模式总是会受到“如何在语言 Y 中进行语言 X 的操作”类型的支持,以支持语言的自然边界。一个完美的例子是单例模式,基本上,我如何在 OOP 中获得 C 全局变量? (我会回答,你不应该)。
      • 我第二个 Kaz:模式不是“解决一次又一次看到的类似问题的方法”,而是“解决一次又一次看到并且必须一次又一次重写的类似问题的方法,因为这种语言不允许一个人只写一次”。换句话说,如果语言允许在库/类/模块等中提取/抽象模式,它就不再是模式,而是成为库/类/模块。在 FP 中,将一些代码提取/抽象为函数要容易得多,因此“模式”更容易转换为可重用代码,从而使其不是模式。
      • 欢迎您的解释,但 GoF 书清楚地定义了一种模式,如果您阅读介绍性章节,它没有说明语言或语言的弱点。当然,某些语言的某些领域会使它们更频繁地利用某些模式,但是无论您将其编写十次(剪切和粘贴)还是使用十次实现(子类化)实现一次,或者将框架配置为稍微执行十次不同的方式,只是暴露模式的一个实现细节。
      • 多年后回到这个对话中,我认为很多人将模式与特定的编程语言或特定的编程范式联系起来。它们可以在这样的上下文中使用,但它们在编程之前就已经存在。 “一种永恒的建筑方式”讨论了建筑建筑和社区规划的模式。这意味着面向模式的技术可以在“语言限制”之外使用,除非您想将构建构造称为编程语言:)
      【解决方案12】:

      正如其他人所说,有一些特定于函数式编程的模式。我认为摆脱设计模式的问题与其说是切换到函数式的问题,不如说是语言特性的问题。

      看看 Scala 如何消除“单例模式”:您只需声明一个 object 而不是类。 另一个特性,模式匹配,有助于避免访问者模式的笨拙。请参阅此处的比较: Scala's Pattern Matching = Visitor Pattern on Steroids

      Scala 和 F# 一样,是面向对象功能的融合。我不知道 F#,但它可能具有这些功能。

      闭包存在于函数式语言中,但它们不必局限于它们。它们有助于使用委托人模式。

      还有一个观察。这段代码实现了一个模式:它是如此经典且如此基本,以至于我们通常不会将其视为“模式”,但它确实是:

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

      Java 和 C# 等命令式语言采用了本质上是函数式的构造来处理这个问题:“foreach”。

      【讨论】:

      • 我会说 Scala 包括对单例模式的一流支持。该模式仍然存在,但与 Java 相比,应用该模式所需的样板代码大大减少。
      • 如果意见像*******,那么...看看其余的答案。 “你只是声明一个对象而不是一个类”是如此真实,但我会明确地将其称为对象文字(即 var singleton = {};)。我也喜欢提到 foreach 模式。不幸的是,看起来大多数回答/评论这个问题的人都不了解函数式编程,而是宁愿证明使用 OOP 设计模式是合理的。 +1 提供具体示例,如果可以的话,我会提供更多。
      • @JacquesB 我无法对 Scala/Haskell 发表评论,但在 JavaScript(即混合函数式/命令式)中,绝对没有样板,您只需使用对象文字语法的组合来调整声明对象的方式,匿名函数,将函数作为第一类成员传递,并允许多重继承(无需接口契约)。
      【解决方案13】:

      GoF Design Patterns 正在为作为 Simula 67 后代的 OO 语言(如 Java 和 C++)编写变通方法。

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

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

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

      就函数式编程而言,模式是否适用于它取决于给定的函数式编程语言是否具有某种对象系统,以及它是否以受益于模式的对象系统为模型。这种面向对象的类型不能很好地与函数式编程混合,因为状态的突变在前面和中心。

      构造和非可变访问与函数式编程兼容,因此与抽象访问或构造有关的模式可能适用:工厂、外观、代理、装饰器和访问者等模式。

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

      【讨论】:

      • “GoF 设计模式是编码变通方法”只是一个错误的陈述。
      【解决方案14】:

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

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

      【讨论】:

        【解决方案15】:

        如果不提出类型系统,就无法进行此讨论。

        函数式编程的主要特征包括函数作为一等值、柯里化、不可变值等。对我来说,OO 设计模式似乎并不明显接近这些特征中的任何一个。

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

        当然,还有一些设计模式是 FP 语言无法解决的。单例的 FP 等价物是多少? (暂时忽略单例通常是一种糟糕的使用模式)

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

        您可以浏览 23 个列表并消除更多,但我现在没有时间。

        【讨论】:

        • 类型类(相当于 OOP 接口的 FP)如何消除对单例(相当于全局状态的 FP)的需要?
        【解决方案16】:

        我认为只有两个 GoF 设计模式旨在将函数式编程逻辑引入自然 OO 语言。我想到了战略和指挥。 其他一些 GoF 设计模式可以通过函数式编程进行修改,以简化设计并保持目的。

        【讨论】:

        • 事实是,许多模式的主要目的是利用多态性来完成对 FP 概念的体面支持可以自动允许的事情。 (例如,我见过的大多数 Builder 的化身只是半途而废的柯里化。)一旦您可以轻松地将函数视为值,模式通常会简化到微不足道的地步。它们变成“传递回调”或“拥有回调字典”——例如,不同的构建器类几乎可以消失。 IMO 模式一旦变得微不足道,就不再是一种模式,只是 事情如何工作,而不是您需要实现的东西。
        【解决方案17】:

        基本上,是的

        • 当模式绕过 ultimalty 促进 composition 的缺失功能(高阶函数、流处理...)时。
        • 需要一次又一次地重写模式的实现本身可以被视为language smell

        此外,这个page (AreDesignPatternsMissingLanguageFeatures) 提供了一个“模式/功能”翻译表和一些很好的讨论,如果你愿意挖掘的话。

        【讨论】:

          【解决方案18】:

          函数式编程不会取代设计模式。设计模式无法替代。

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

          【讨论】:

          • 设计模式无法替代?我认为这有点封闭。我们可能都同意设计模式旨在解决编程问题,我至少希望有一天我们可以在没有设计模式的情况下解决这些问题。
          • 任何特定的模式都可以被替换,但模式的概念不能。请记住,“模式”一词出现在架构领域。
          • 模式并不是为了解决编程问题。模式是我们编程的方式。模式文档旨在帮助解决编程问题。
          • @Torbjørn:模式是我们当语言妨碍时的编程方式。它们的存在是因为程序的期望行为和语言的内置能力之间存在一些不匹配,其中需求和能力不能很好地映射或映射不明确。如果不是这样,就没有模式;您将有一个实现只是事情是如何完成的,而其他实现实际上不值得考虑。
          • 除非模式真正存在只是为了促进交流。没有其他目的。在这些年来我参加的所有设计会议中,对算法的讨论才是重要的,而不是模式。这种模式很少能以任何有意义的方式解释真正发生的事情。它是否准确解释了 O(n) 与 O(n Log(n)) 的影响?不。它是否解释了它与现有架构的适应程度?没有。全尺寸算法讨论可以。我并不是说模式本身应该被淘汰,但如果他们被淘汰,那么几乎没有任何事情会因此受到影响。
          【解决方案19】:

          在 2013 年的新书“函数式编程模式 - Scala 和 Clojure 中”作者 Michael.B. Linn 在许多情况下对 GoF 模式进行了比较并提供了替代方案,并且还讨论了新的功能模式,例如“尾递归”、“记忆”、“惰性序列”等。

          这本书在亚马逊上有售。拥有数十年的面向对象背景,我发现它非常有用且令人鼓舞。

          【讨论】:

            【解决方案20】:

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

            由于在真正的函数式编程中不存在状态,因此应用 GoF 模式是没有意义的。没有功能性设计模式,就像有 GoF 设计模式一样。与现实相比,每个函数式设计模式都是人工的,因为函数是数学而非现实的构造。

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

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

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

            因为真正的函数式编程不需要 GoF 模式,这并不意味着不应该应用 SOLID 原则。 SOLID 原则超越了任何语言范式。

            【讨论】:

            • FP 可以有状态——只是没有全局、共享或可变状态。
            【解决方案21】:

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

            但是,有些模式非常普遍,我能想到的所有编程平台都应该有。这是一个(不完整的)列表:

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

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

            • 解释器。一般来说,任何程序都只做两件事:解析输入和打印输出。鼠标输入需要解析,窗口部件需要打印出来。因此,拥有一个嵌入式解释器为程序提供了额外的自定义功能。

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

            • Monad 转换器和装饰器。前者用于为现有的 monad 添加额外的能力,后者为现有的对象添加额外的能力。

            【讨论】:

              【解决方案22】:

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

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

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

              【讨论】:

                【解决方案23】:

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

                但是,这两种范式不一定是 100% 矛盾的,可以一起应用以从两个世界中受益。

                即使使用像 C# 这样本身不支持函数式编程的语言,如果您了解 FP 原理,您也可以编写函数式代码。同样,如果您了解 OOP 原则、模式和最佳实践,则可以使用 F# 应用 OOP 原则。无论您使用哪种编程语言,您都会根据您尝试解决的情况和问题做出正确的选择。

                【讨论】:

                  【解决方案24】:

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

                  根据我使用多范式语言 (Ruby) 开发的经验,FP 实现在简单的情况下运行良好,但在上下文更复杂的情况下,基于 GoF OOP 的方法更适合。

                  FP 方法不会取代 OOP 方法,而是对其进行补充。

                  【讨论】:

                    【解决方案25】:

                    在函数式编程中,设计模式有不同的含义。事实上,大多数 OOP 设计模式在函数式编程中是不必要的,因为更高级别的抽象和 HOF 用作构建块。

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

                    【讨论】:

                      【解决方案26】:

                      恕我直言,函数式编程的最重要特征是,您只使用 表达式 进行编程——表达式中的表达式中的表达式都计算为最后一个最终表达式,“当评估”。

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

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

                      【讨论】:

                      • 嗯,我应该注意到这个问题是十一岁才回答的。 :-)
                      【解决方案27】:

                      振作起来。

                      听到我声称已经替换了设计模式并揭穿了 SOLID 和 DRY,这会让很多人感到更加愤怒。我谁都不是。尽管如此,我还是正确地建模了协作(制造)架构,并在我的网站http://www.powersemantics.com/ 上发布了在线构建流程的规则以及其背后的代码和科学。

                      我的论点是,设计模式试图实现制造所称的“大规模定制”,这是一种可以重塑、重组和扩展每个步骤的过程形式。您可能会将此类进程视为未编译的脚本。我不会在这里重复我的(在线)论点。简而言之,我的大规模定制架构通过在没有任何混乱语义的情况下实现灵活性来取代设计模式。我很惊讶我的模型运行得如此之好,但程序员编写代码的方式根本无法与制造业如何组织协作工作相提并论。

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

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

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

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

                      【讨论】:

                        【解决方案28】:

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

                        【讨论】:

                          猜你喜欢
                          • 2012-01-15
                          • 1970-01-01
                          • 2020-09-18
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2013-06-05
                          • 2013-05-31
                          • 1970-01-01
                          相关资源
                          最近更新 更多