原文: The Forgotten History of OOP. Most of the programming paradigms we… | by Eric Elliott | JavaScript Scene | Medium
原文译文: 译 被遗忘的面向对象编程史(软件编写)(第十六部分) - 掘金
面向对象来源
- “面向对象编程”(OOP)是 Alan Kay(艾伦·凯) 在 1966 年或 1967 年读研究生时创造的。
- 提出于 1965 年的
simula语言,是第一个被广泛认可的“面向对象”的编程语言。与 Sketchpad 一样,Simula 使用了对象,甚至还引入了类,类继承,子类和虚方法。 - “面向对象编程”(OOP)这个想法是想在软件中使用封装好的微型计算机,通过消息传递而不是直接的数据共享,以此来防止将程序分裂为单独的“数据结构“和“程序”。
面向对象当初的指代
“我很抱歉,我很久以前为这个话题创造了“对象”一词,而因为它让很多人专注于细枝末节。实际上真正重要的是消息传递。”
“OOP 对我来说意味着消息传递,对状态进程的本地保留保护和隐藏,以及对所有事物的动态绑定。”
~ Alan Kay
根据 Alan Kay 的说法,OOP 的基本要素是:
- 消息传递
- 封装
- 动态绑定
消息传递和封装的结合有一些重要的目的:
- 避免共享可变状态
通过封装状态并将其他对象与本地状态更改隔离开来。影响另一个对象状态的唯一方法是通过发送消息来请求(而不是命令)该对象来更改它。状态变化由局部控制,,而不是暴露于共享访问。 - 解耦
通过消息传递 API,消息发送者与消息接收者松散耦合。 - 适应变化
通过动态绑定在运行时适应变化
为什么会有这样的面向对象
- Alan Kay 的生物学背景
想象软件运行在巨大的分布式计算机(互联网)上,个人计算机就像生物细胞一样,在自己的孤立状态下独立运行,并通过消息传递进行通信。 -
“ 我意识到, 将细胞与整个计算机类比, 能摆脱 data
_“I realized that the cell_whole-computer metaphor would get rid of data[…]”
~ Alan Kay/ - 通过
rid of dataAlan Kay 意识到::共享可变状态导致的问题和耦合问题::, 原因在于共享的数据By “get rid of data”, Alan Kay was surely aware of shared mutable state problems and tight coupling caused by shared data — common themes today. 此处原文应该翻译错了, /shared data/ 指的应该是 /shared mutable state problems/ + /tight coupling/- 这两个问题仍然是今天的普遍问题
- ARPA 程序员对在程序编写之前选择数据模型的需求感到沮丧
这篇论文指出, 程序与特定数据结构高度耦合导致程序没有变化修改空间Procedures that were too tightly coupled to particular data structures were not resilient to change. 此处原文我也倾向于翻译的不准确, `使得程序无法适应变化` 的翻译太过抽象, 指的应该是程序在外界需求变化后, 难以修改维护
封装
- 对象可以被抽象出来并隐藏数据结构的实现方法
- 对象的内部实现可以在不破坏软件系统其他部分的情况下进行更改
↓
通过迟约束,完全不同的计算机系统可以接管对象的功能,并且软件可以继续工作
与此同时,对象可以公开一个标准接口,该接口可以处理对象在内部使用的任何数据结构
我目前尚未能了解当时遇到的问题, 所以无法知道 OOP 被提出所要解决的问题
但能知道的是, OOP 这个概念被提出来时, 目的是不做数据共享, 而是通过消息传递的方式
对象的代数特性
- Alan Kay 还将对象视为代数结构,这些结构可以通过数学来证明它们的行为:
“我的数学背景使我意识到每个对象可能有几个与之相关的代数式,可能有代数式族,而这些将会非常有用。” ~ Alan Kay - 允许对象提供公式化验证,确定性行为和改进的可测试性,因为代数本质上是遵循方程式规则的操作。
- 在程序员术语中,代数就像是由函数(操作)组成的抽象,这些函数必须通过单元测试强制执行的特定规则(公理/方程式)。
联系到 JS
- JavaScript 是针对世界对面向对象编程的误解的一种反击
- Smalltalk 和 JavaScript 都支持:
- 对象
- 主类函数和闭包
- 动态类型
- 迟绑定(函数/方法在运行时可更改)
- 不支持类继承的面向对象语言
引申出前文两个概念
什么是面向对象编程的必要特性(根据 Alan Kay 的说法)?
- 封装
- 消息传递
- 动态绑定(程序在运行时进化/适应的能力)
什么是非必要的特性?
- 类
- 类继承
- 对象_函数_数据的特殊处理
- new关键字
- 多态性
- 静态类型
-
将类识别为“类型”
- Java 或 C# 的开发者,您可能会认为静态类型和多态性是必不可少的成分
- 但Alan Kay 倾向于以代数形式处理共性行为
多态是用于描述程序共性的, 特别是强类型,静态类型语言中, Alan Kay 认为, 共性应该由代数式描述
例如: fmap :: (a -> b) -> f a -> f b, 用映射来替代多态
// isEven = Number => Boolean
const isEven = n => n % 2 === 0;
const nums = [1, 2, 3, 4, 5, 6];
// map takes a function`a => b` and an array of `a`s (via `this`)
// and returns an array of `b`s.
// in this case, `a` is `Number` and `b` is `Boolean`
const results = nums.map(isEven);
console.log(results); // [false, true, false, true, false, true]
- 大多数类型系统有太多的限制,无法自由表达动态和函数想法
什么是对象
- 多年来,对象已经被赋予大量的含义
- JavaScript 中称之为“对象”的只是复合数据类型
-
可以通过并且通常都会支持给 JS 的对象增加封装, 消息传递,通过方法进行行为共享,甚至子类多态, 但所有这些都是选择加入的行为。
- 我们现在认为对象只是一个复合数据结构,并不需要其他更多的含义。
- 但这样的定义, 并不会比函数式编程 更加 “”面向对象”
作者认为
- 现代编程语言中的“对象”意味的比 Alan Kay 设想的要少得多
- 这里作者用
组件这个词来指代 Alan Kay 所表达的对象 - 作者认为: 在 JavaScript 中操作
objects或使用class inheritance并不意味着在“面向对象” - 真正的面向对象的含义:
- 使用
组件编程(就是 Alan Kay 所说的“对象”) - 必须封装
组件状态 - 使用消息传递进行对象间通信
- 组件可以在运行时被创建_更改_替换
- 这么去使用组件, 意味着面向对象
然后作者遵循流行用法, 将这种重新取名(因为现在的面向对象已经脱离原本意义, 但又占有了面向对象这个词)
- 所以也许我们应该放弃面向对象并将其称为“面向消息的编程(MOP)”而不是“面向对象的编程(OOP)”
-
好的 MOP 意味着系统是通过消息调度与其他组件通信,不是各个系统去直接操纵彼此的状态。
-
用户操作按钮 -> 调动保存消息 -> 状态组件解析 -> 转发到状态更新处理程序 -> 向界面组件发送”状态更新”消息 -> 界面组件解析状态协调更新
- 这些系统不需要知道系统其他部分的细节。只需要关注自身的模块化问题
- 系统组件是可分解和可重新组合的。
- 它们实现标准化接口,以便它们能够相互操作
- 同一个软件系统的组件甚至都不需要位于同一台机器上。系统可以是分布式的
结论:
现在是时候让软件世界放弃失败的类继承实验了,转而接受最初定义面向对象时所秉承的数学和科学精髓。
现在是时候开始构建更灵活,更具弹性,更容易组合的软件了,让 MOP 和函数编程协调工作。