函数与对象
关于面向对象编程 (OOP) 和函数式编程 (FP) 之间的区别一直存在争议。我们不妨换个角度,谈谈它们的相似之处。让我们先来谈谈它们的主要组成部分:函数和对象。
如果我不偷懒的话,这将会是一个系列帖子。
什么是物体?
我尝试寻找一个好的定义,但这比我想象的要难得多。很多资料都在讨论什么是面向对象编程(OOP),但没有人费心解释什么是对象。
我想,我们还是采用Java 中的对象定义吧:
对象是理解面向对象技术的关键。环顾四周,你会发现许多现实世界中的物体:你的狗、你的桌子、你的电视机、你的自行车。
现实世界中的物体都具有两个共同特征:它们都具有状态和行为。例如,狗有状态(名字、颜色、品种、饥饿状态)和行为(吠叫、叼东西、摇尾巴)。自行车也有状态(当前档位、当前踏频、当前速度)和行为(换档、改变踏频、刹车)。识别现实世界物体的状态和行为是开始学习面向对象编程的绝佳方法。
这个定义很容易理解。我稍微改写一下。对象是一种状态,它附带一种行为。
什么是函数?
我为此写了两篇博文:
让我们采用简化的定义(与对象定义类似),即函数是一种行为(有关精确定义,请参阅上面的链接)。
在函数式编程中,他们喜欢将函数作为值传递,为了实现这一点,函数被“转换”为闭包(这里的“转换”一词并不准确,因为闭包是一个带有自由变量的函数,但我们采用简化的视角)。
(编程语言中的)闭包是什么?
闭包是一种同时包含代码和数据的数据结构。
我稍微改写一下。闭包(或函数即值)是一种带有状态的行为。(这里的状态是不可变的。我把任何数据都称为状态。)
等一下🤔
再次比较这两个定义:
- 该对象是一种状态,并附加了一种行为。
- 闭包(或函数即值)是一种带有状态的行为。
它们不是一样的吗?
我不相信。你的证据是什么?
我们来写一些代码。我将使用 JavaScript,因为它同时支持这两种编程范式。
class DogClass {
#name;
constructor(name) {
this.#name = name;
}
bark() {
return `${this.#name} is a good dog!`;
}
}
const belka = new DogClass('Belka');
belka.bark();
注意:本示例使用“JavaScript 类字段声明”提案来声明私有字段名。截至发稿时,该示例在 Chrome 浏览器中运行正常。
const DogFunction = (name) => {
return {
bark: () => {
return `${name} is a good dog!`;
}
}
}
const strelka = DogFunction('Strelka');
strelka.bark();
注意:该函数返回记录数据结构(在 JavaScript 中被误称为“对象”,但我们并未使用它的任何“对象”特性,而是将其用作简单的键值数据结构)。变量name私有地存储在闭包的作用域内,无法在外部访问。
这并非新观点
仔细想想,这很有道理:所有计算机都处理状态(数据)和行为。这个观点被反复验证:
以下是兰波特对计算的定义:
计算有多种定义方式。现在,我采用最简单的定义:计算是一系列步骤,我称之为行为。对于“步骤”的定义,通常有三种选择,从而产生三种不同的行为:
- 动作行为。步骤是一种动作,它只是一系列动作中的一个元素。动作行为则是一系列动作的集合。
- 状态行为。步骤是一对
(s, t)状态,其中状态是某个状态集合中的一个元素。状态行为是一系列s1 → s2 → s3 → · · ·状态。步骤表示从一个状态到另一个状态的(si, si+1)转换。sisi+1- 状态-动作行为。步骤是一个三元组
(s, α, ti),其中s和t是状态,α是动作。状态-动作行为是一个序列s1 -α1→ s2 -α2→ s3 -α3→ · · ·。步骤表示由动作 执行的状态间(si, αi, si+1)转换。sisi+1αi——计算与状态机。莱斯利·兰波特,2008年4月19日
Wirth 撰写了《算法 + 数据结构 = 程序》一书。
Ray Toal 写道:类型由一组值和一组允许的操作组成。
PS
我们还没讨论到的是变异(mutation)的问题。在“纯”函数式编程(FP)中,变异是不允许的。但在面向对象编程(OOP)中,变异是允许的。我所说的“纯”指的是使用惰性求值和IO monad的lambda演算,这是一个比较狭窄的领域¯\_(ツ)_/¯。
文章来源:https://dev.to/stereobooster/function-vs-object-1pe3图片来自 Unsplash 用户 NordWood Themes