面向对象编程,函数式编程,过程式编程

这些编程范式之间的区别是什么,它们是否更适合于特定的问题,或者是否有任何用例偏爱其中一种?

非常感谢架构示例!

200297 次浏览

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

例如,如果你正在制作一款电子游戏,c++中有更多好的代码示例和sdk,所以你可能会更好地使用它。对于一个小型的web应用程序,有一些很棒的Python、PHP和Ruby框架可以让你快速启动和运行。对于大型项目来说,Java是一个很好的选择,因为它有编译时检查以及企业库和平台。

过去的情况是,不同语言的标准库都很小,很容易复制——C、c++、Assembler、ML、LISP等等。有了基础,但当涉及到网络通信、加密、图形、数据文件格式(包括XML)等标准化时,它往往会退缩,甚至像平衡树和哈希表这样的基本数据结构也被遗漏了!

像Python、PHP、Ruby和Java这样的现代语言现在都提供了更体面的标准库,并且有许多可以轻松使用的优秀第三方库,这在很大程度上要感谢它们采用了名称空间来防止库之间发生冲突,并采用了垃圾收集来标准化库的内存管理方案。

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

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

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

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

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

需要明确的是,每种范例的“优势”仅仅在于算法和数据结构的建模。例如,如果您的算法涉及列表和树,那么函数式算法可能是最合理的。或者,例如,如果您的数据是高度结构化的,如果这是您的语言的原生范式,那么将其组合为对象可能更有意义——或者,它也可以轻松地编写为monads的功能抽象,这是Haskell或ML等语言的原生范式。

您使用的选择只是对您的项目和您的语言支持的抽象更有意义。

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

  1. 理解不同架构风格/模式的特点。
  2. 理解不同编程范式的特点。

Wikipeida的软件架构文章中显示了软件架构样式/模式的列表。你可以很容易地在网上查到。

简而言之,一般来说,过程式适用于遵循过程的模型,OOP适用于设计,而函数式适用于高级编程。

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

在理解它们之后,您可以将体系结构样式/模式的条目与编程范例联系起来。

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

我也不认为这是语言的问题。你可以用几乎任何流行的语言编写函数式的、过程式的或面向对象的,尽管在某些语言中可能需要额外的努力。

我认为它们通常不是“相对”的,但你可以把它们结合起来。我还认为,你经常提到的词语只是流行语。很少有人真正知道“面向对象”是什么意思,即使他们是它最狂热的传播者。

这些范式并不一定是相互排斥的。如果你看python,它支持函数和类,但同时,所有东西都是对象,包括函数。你可以在一段代码中混合和匹配函数式/oop/过程式风格。

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

而在像C这样的过程式语言中,传递函数的唯一方法是使用函数指针,仅靠这一点并不能完成许多功能强大的任务。

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

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

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

我的一个朋友正在用NVIDIA CUDA写一个图形应用程序。应用程序很好地适应了面向对象的范式,并且可以将问题整齐地分解为模块。然而,要使用CUDA,你需要使用C,它不支持继承。因此,你需要聪明。

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

i)你可以使用钩系统,它期望父函数P的每个子函数C对函数f有一定的覆盖。你可以让子函数注册他们的覆盖,这些覆盖将被存储并在需要时调用。

ii)你可以使用struct 内存对齐特性将子类型转换为父类型。

这可能很简单,但要想出经得起考验的、可靠的解决方案并不容易。您将花费大量时间设计系统,并且无法保证您不会在项目中途遇到问题。实现多重继承更加困难,如果不是几乎不可能的话。

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

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