函数式编程与OOP的内容及主要区别

1,151 阅读11分钟

Functional programming vs OOP: comparing paradigms

函数式编程和面向对象编程(OOP)有非常不同的编程方法。本文详细解释了每种编程范式的内容,并进一步总结了它们的主要区别。

目录

编程范
功能性编程
面向对象编程
纯粹的函数
对象和类
可变与不可变
强制性与声明性
功能性编程与OOP:关键的区别
结论

编程范式

编程范式是解决某个编程任务的一种方法(或方式)。范式代表了开发人员在开发软件时可以实施的不同策略原则规则。每一种现有的编程语言(有很多)都必须遵循至少一种编程范式。尽管有差异,但所有范式都有其优点和缺点,这就是为什么越来越多的流行编程语言喜欢提供多范式编程,而不是严格地只遵循一种范式。然而,这通常归结于开发者的偏好和应用程序的目标。

此外,很少有一种 "纯粹 "的语言只支持100%的一种范式。例如,众所周知,Java是一种面向对象的编程(OOP)语言,但它是100%的OOP吗?好吧,也许不是100%,但接近,这足以维持它作为 "最纯粹的 "OOP语言之一的声誉。

如今,我们可以遵循的编程范式数量惊人。简而言之,有两种主要的范式--命令式和声明式,它们影响着多种编程范式。在一些最流行的范式中,我们有逻辑编程范式、程序性编程范式,当然还有函数式和面向对象的编程。

功能性编程

函数式编程是一种声明式的编程范式,它以纯函数的方式编写,这意味着这些函数不修改变量,而是生成新的变量作为输出。换句话说,纯函数的输出只取决于输入参数;因此,不存在外部影响,这就避免了副作用。此外,编写纯函数也有助于开发人员避免易变数据和共享状态。

因此,函数式编程有很多好处,并被用于很多编程语言和框架中。它是一种流行的编程范式,因为它能够通过使用函数来创建可维护的和干净的软件,这对代码组织至关重要。

面向对象的编程

面向对象的编程是一种编程范式,它基于类和对象的概念来组织数据和软件结构。

是一组指令(或蓝图),为特定的对象建立数据结构,确定对象将包含什么(对象中可以存在的变量类型)以及它将如何表现(定义如何操作变量的方法或成员函数)。因此,对象是类的实例,因为类作为 "模板 "来创建对象。另外,对象可以包含字段形式的数据(也被称为属性)和程序形式的代码(也被称为方法)。

纯粹的函数

如前所述,函数式编程依赖于函数,而面向对象的编程则基于类和各自的对象。一个函数是一个过程,它检索数据输入,处理它,然后返回一个输出。因此,函数是为实现某种任务而编写的代码模块。纯函数是 "纯 "的,因为它们对于相同的参数值总是返回相同的输出。

此外,纯函数的应用没有副作用,因为没有与局部静态变量、可变参考参数、输入流或其他外部方面发生变化。它们完全独立于任何状态,它们只需要输入。这个特性有四个主要的优点。

  • 良好的可读性和理解力,因为它们是原子性的。
  • 纯函数是跨分布式计算集群和CPU并行处理的良好解决方案。
  • 由于纯函数是独立的,所以在代码中重构和重组它们更容易。另外,独立于外部也使它们更具有可移植性,更容易在其他应用程序中重复使用
  • 纯函数可以很容易地被测试,考虑到所需要的只是测试输入和确认(预期)结果。

因此,纯函数是非常简单和可重用的代码块,在实现一个程序时可以非常实用。因此,函数是函数式编程的主要单元是非常合理的。尽管在OOP中可以创建纯函数,但它并不是这种范式的主要焦点,因为它的主要单元是对象,而对象的设计又是为了与对象的状态进行交互。

纯函数的缺点是,它将操作置于数据之上。如果一个纯函数只产生与输入相同的输出,那么它就不能返回其他不同的(也许是有意义的)值。由于这个原因,函数式编程具有极强的操作性、实用性,而且正如其名称所示,是功能性的。

对象和类

面向对象的编程在很大程度上依赖于类和对象的概念,而类和对象又包含函数和数据。正如所解释的,类是一个既定的蓝图(或原型),对象就是从这个蓝图中建立起来的。因此,类代表了某一对象类型所共有的一组方法(或属性)。反过来,一个对象是OOP的基本单位,代表现实生活中的实体。一个对象必须有。

  • 一个身份一个唯一的名字;拥有一个唯一的ID可以使对象与其他对象进行交互。
  • 一个状态一个对象的状态反映了一个对象的属性或特性。
  • 行为一个对象的方法,以及对象将如何响应并与其他对象互动。

例如,让我们想象一下,我们有 "运动员1 "这个对象,在这个对象中,我们通过属性拥有关于这个对象的所有数据。因此,状态可以是运动、身高、体重、奖杯、国家等等。这些属性存储了数据,而一个对象的数据可以通过归属于一个对象的函数来操作。在这种情况下,这个对象的方法可以是攻击、防御、跳跃、跑步、冲刺等。此外,开发者可以通过在对象的代码模块中声明变量来创建属性。

总之,在OOP语言中,数据被存储在属性中,而背后的逻辑在于函数和各自的方法中。关于面向对象的编程,方法是属于一个类或对象的功能;方法是由一个特定的类甚至对象**"拥有"**。相比之下,函数是 "自由 "的,意味着它们可以在代码的任何其他范围内,不属于类或对象。因此,一个方法总是一个函数,但一个函数不总是一个方法。当对象包含紧密合作的属性和方法时,这些对象属于同一个类。

在OOP语言中,编写代码是为了定义类,并由此定义各自的对象。纯粹的面向对象语言遵循四个核心原则:封装、抽象、继承和多态性。

Functional programming vs OOP: comparing paradigms

让我们先来关注一下封装。封装在OOP中是非常重要的,因为它包括将类中的变量封装起来不被外界访问的能力。 属性和方法可以是私有的或公共的。OOP语言允许开发者建立多种程度的可见性。一方面,私有特性只能对类本身可见。另一方面,公共特性可以对所有人可见。

继承也是非常重要的,因为它提供了一个组织和结构软件的机制。它允许类从它们的超类中继承状态和行为,这也意味着这一原则支持重用性。

可变的与不可变的

面向对象编程可以支持可变数据。相反,函数式编程则使用不可变的数据。在这两种编程范式中,不可变的对象指的是一个一旦创建就不能修改其状态的对象。可变的对象则正好相反;一个对象的状态甚至在创建后也可以被修改。

在纯函数式编程语言(例如Haskell)中,不可能创建可变的对象。因此,对象通常是不可变的。在OOP语言中,答案并不那么直接,因为它更多地取决于每种OOP语言的规范。为了提高运行时的效率以及可读性,字符串和具体对象可以被表达为不可变的对象。另外,在处理多线程应用程序时,不可变的对象会非常有帮助,因为它避免了数据被其他线程改变的风险。

可变对象也有其优势。它们允许开发者直接在对象中进行修改,而不需要分配对象,从而节省了时间,加快了项目的进度。然而,这要由开发者和开发团队根据项目的目标来决定它是否真的有回报。例如,变异也会为bug打开更多的大门,但有时它的速度是非常合适的,甚至是必要的。

因此,OOP可以支持可变性,但其语言也可能允许不可变性。Java、C++、C#、Python、Ruby和Perl可以被认为是面向对象的编程语言,但它们并不完全支持可变性或不可变性。例如,在Java中,字符串是不可变的对象。尽管如此,Java也有字符串的可变版本。同样地,在C++中,开发者可以将新的类实例声明为不可变的或可变的。另一个很好的例子是Python,它的内置类型是不可变的(例如,数字、布尔、frozensets、字符串和图元);然而,自定义类通常是可变的。

同样重要的是要记住,许多提到的语言不是100%的函数式编程或面向对象。例如,Python是最流行的语言之一,它确实是一种多范式的语言。因此,它可以根据开发者的偏好,采用更多的函数式或OOP方法。

强制式与声明式

声明式编程是一种编程范式,它声明了程序需要完成的任务。它并不声明程序在整个控制流中应该如何完成某种计算;它只是声明它想要什么,而不解释如何得到它。相比之下,命令式编程依靠一系列的语句来修改程序的状态,为每一步提供详细的描述,说明如何完成某个目标。

大多数的OOP语言被设计成主要遵循命令式编程。相比之下,函数式编程倾向于遵循更多的声明式编程方法,因为它的逻辑并没有明确地描述流程控制以实现某种输出。相反,它将计算表达为一个纯函数。

函数式编程与OOP:关键的区别

函数式编程OOP

一个函数是主要单位。

对象是主要单位。

纯粹的函数没有副作用。

方法可能有副作用。

遵循更多的声明式编程模型。

主要遵循命令式的编程方式。

在纯函数式编程语言中,不可能创建可变的对象。因此,对象通常是不可变的。

在OOP语言中,答案并不那么直接,因为它更多地取决于每种OOP语言的规范。因此,OOP可以同时支持可变和不可变的对象。

函数式编程写的是纯函数。纯函数只产生与输入相同的输出。因此,函数式编程具有极强的操作性、实用性,而且正如其名称所示,是功能性的。

OOP不像函数式编程那样具有操作性。事实上,OOP将数据存储在对象中,数据的优先级高于操作。

结论

函数式编程和OOP是两种最流行和被遵循的(即使只是部分)编程范式。尽管有不同的方法,但两者都是为了帮助开发者创建高效和高质量的应用程序。这些范式只是有不同的方法--通过 "方法",我们指的是策略、原则和规则--来达到目的。

一方面,在OOP中,数据被存储在对象中,因为数据和各自的行为(意思是,一个程序可以对数据做什么或用数据做什么)应该属于一个单一的位置。

另一方面,在函数式编程中,数据是由函数传递和收集的。然而,它并不将数据存储在对象中,因为这可能会影响到清晰度,考虑到对于函数式编程,数据和行为是不同的。

这两种编程范式都有其优点和缺点,这就是为什么许多开发人员实际上更愿意根据每个项目的要求和目标来实施混合解决方案