关于范例:OOP与功能编程与程序

OOP vs Functional Programming vs Procedural

这些编程范例之间的区别是什么,它们是否更适合于特定的问题,或者任何用例比其他用例更倾向于一个?

建筑实例不胜感激!


所有这些问题都有各自的优点——它们只是解决相同问题的不同方法。

在纯过程风格中,数据往往与操作它的函数高度分离。

在面向对象的样式中,数据往往带有一组函数。

在函数样式中,数据和函数倾向于彼此拥有更多的共同点(如Lisp和Scheme),同时在函数的实际使用方式方面提供更多的灵活性。算法也倾向于用递归和组合来定义,而不是用循环和迭代来定义。

当然,语言本身只影响哪种风格更受欢迎。即使在像haskell这样的纯函数语言中,您也可以用过程样式(尽管这是非常不鼓励的),甚至在C这样的过程语言中,您也可以用面向对象的样式(例如在GTK+和EFL API中)编程。

清楚地说,每个范例的"优势"仅仅在于对算法和数据结构的建模。例如,如果您的算法涉及列表和树,那么函数算法可能是最明智的。或者,例如,如果您的数据是高度结构化的,将其作为对象组合可能更有意义,如果这是您的语言的本机范式——或者,它可以像Monads的功能抽象一样容易编写,Monads是Haskell或ML等语言的本机范式。

您所使用的选项对于您的项目和您的语言支持的抽象更为合理。


我认为现在可用的库、工具、示例和社区完全胜过这种模式。例如,ML(或其他任何语言)可能是最终的通用编程语言,但是如果你不能为你正在做的事情找到任何好的库,你就完蛋了。

例如,如果你正在制作一个视频游戏,在C++中有更多的好的代码示例和SDK,所以你可能会更好。对于一个小型的Web应用程序,有一些很棒的python、php和ruby框架可以让您很快离开并运行起来。Java是大型项目的一个很好的选择,因为编译时检查和企业库和平台。

过去的情况是,不同语言的标准库很小,很容易复制——C、C++、汇编程序、ML、LISP等等。虽然有一些基础知识,但当涉及到网络通信、加密、图形、数据文件格式(包括XML)等方面的标准化时,往往会被排除在外,甚至像平衡树和哈希表这样的基本数据结构也被排除在外!

像Python、PHP、Ruby和Java这样的现代语言现在有了一个更为体面的标准库,并有许多很好的第三方库,您可以很容易地使用它们,这很大程度上得益于它们采用了名称空间来防止库相互冲突,以及垃圾收集来规范库的内存管理方案。


这些范例不必互相排斥。如果您看一下python,它支持函数和类,但同时,一切都是一个对象,包括函数。您可以在一段代码中混合和匹配功能/oop/procedural样式。

我的意思是,在功能语言中(至少在Haskell,我唯一研究过的语言),没有语句!函数中只允许有一个表达式!!但是,函数是一流的公民,你可以把它们作为参数传递给其他一些能力。他们可以用几行代码做一些功能强大的事情。

虽然在C这样的过程语言中,传递函数的唯一方法是使用函数指针,而仅此一种方法并不能实现许多强大的任务。

在Python中,函数是第一类公民,但它可以包含任意数量的语句。所以您可以有一个包含过程代码的函数,但是您可以像函数语言一样传递它。

OOP也是如此。像Java这样的语言不允许您在类之外编写过程/函数。传递函数的唯一方法是将其包装在实现该函数的对象中,然后传递该对象。

在Python中,您没有这个限制。


对于GUI,我认为面向对象的范式非常适合。窗口是一个对象,文本框是对象,OK按钮也是对象。另一方面,像字符串处理这样的工作可以用更少的开销完成,因此使用简单的过程范式更简单。

我认为这也不是语言问题。您可以用几乎任何流行的语言编写功能性、过程性或面向对象的语言,尽管在某些语言中可能需要做一些额外的工作。


为了回答您的问题,我们需要两个要素:

  • 了解不同建筑风格/模式的特征。
  • 了解不同编程范例的特点。
  • 软件架构风格/模式列表显示在维基百科的软件架构文章中。你可以在网上很容易地研究它们。

    简而言之,过程对于遵循过程的模型很好,OOP对于设计很好,而功能对于高级编程很好。

    我认为你应该试着阅读每一个范例的历史,看看为什么人们创造了它,你可以很容易地理解它们。

    在理解了这两者之后,您可以将体系结构样式/模式的项目链接到编程范例。


    我认为它们通常不是"对"的,但是你可以把它们结合起来。我也认为,通常,你提到的词只是流行语。很少有人真正知道"面向对象"是什么意思,即使他们是其中最凶猛的布道者。


    我的一个朋友正在使用Nvidia Cuda编写一个图形应用程序。应用程序非常适合OOP范式,并且可以将问题整齐地分解为模块。但是,要使用CUDA,需要使用C,这不支持继承。因此,你需要聪明。

    a)你设计了一个聪明的系统,在一定程度上模拟继承。这是可以做到的!

    i)您可以使用一个钩子系统,它期望父级p的每个子级c对函数f都有一个特定的覆盖。您可以使子级注册它们的覆盖,这些覆盖将在需要时被存储和调用。

    ii)可以使用结构内存对齐功能将子对象强制转换为父对象。

    这可以是整洁的,但它不容易想出未来证明,可靠的解决方案。您将花费大量时间设计系统,并且无法保证在项目的中途不会遇到问题。实现多重继承甚至更加困难,如果不是几乎不可能的话。

    b)您可以使用一致的命名策略,并使用分而治之的方法来创建程序。它不会有任何继承,但由于您的函数很小、易于理解和格式一致,所以您不需要它。您需要编写的代码数量不断增加,很难集中精力,而且不屈服于简单的解决方案(黑客)。然而,这种忍者的编码方式是C编码方式。在低级自由和编写好代码之间保持平衡。实现这一点的好方法是使用函数语言编写原型。例如,Haskell非常适合于原型算法。

    我倾向于使用方法B。我使用方法A编写了一个可能的解决方案,而且老实说,使用该代码感觉非常不自然。