追溯法 —— 浅谈面向对象

1,060 阅读6分钟

面向对象是现在特别流行的编程范式,每个程序员都将面向对象挂在嘴边,而且主流的编程语言都宣称自己是 OOPL(面向对象编程语言),而网上对 OO(面向对象 Object Oriented)的解释也是众说纷纭,本篇文章就通过追本溯源的方式,尝试找到最开始提出 OO 的计算机科学家是怎么想的。

我的印象

最开始接触编程是大学的 C 语言课程,只有 if else loop 和「指针」没有所谓的对象。

大学毕业之后自学了 C#,看了「刘铁锰」老师的视频,介绍了「类」 —— 是对现实事物进行抽象所得到的结果,而「对象」—— 也叫实例,是类经过「实例化」后得到的内存中的实体(在现实世界中叫对象,在程序中叫做实例)。

之后做 Android 看了《Java 编程思想》和《Java 核心技术》,介绍“纯粹”的面向对象程序设计方法是什么样的:

  1. 所有东西都是对象;
  2. 程序是一大堆对象的组合;
  3. 每个对象都有自己的存储空间,可容乃其他对象;
  4. 每个对象都有一种类型;
  5. 同一类所有对象都能接收相同的消息。

后来做 Web 开发,看了 MDN 的文档,对象 —— 是一个包含数据和方法的集合。在 JS 中一切都是对象,想要使用直接通过字面量 {} 就能得到对象。每一个对象都拥有一个「原型对象」,对象可以通过原型链找到公共的属性和方法。

什么是对象?

要弄清楚面向对象之前,先要搞清楚什么是对象,winter 老师在「重学前端」这样介绍对象。

我们先来说说什么是对象,因为翻译的原因,中文语境下我们很难理解“对象”的真正含义。事实上,Object(对象)在英文中,是一切事物的总称,这和面向对象编程的抽象思维有互通之处。

对象这一概念在人类的幼儿期形成,这远远早于我们编程逻辑中常用的值、过程等概念。在幼年期,我们总是先认识到某一个苹果能吃(这里的某一个苹果就是一个对象),继而认识到所有的苹果都可以吃(这里的所有苹果,就是一个类),再到后来我们才能意识到三个苹果和三个梨之间的联系,进而产生数字“3”(值)的概念。

在《面向对象分析与设计》这本书中,Grady Booch 替我们做了总结,他认为,从人类的认知角度来说,对象应该是下列事物之一:

  • 一个可以触摸或可以看见的东西;
  • 在智力上可以理解的东西;
  • 可以指导行动或者思想的东西。

看的这三点之后不知道你是不是跟我一样的感受,哦,知道了,然后呢?这三点看起来像哲学的总结,对于我们编程到底有什么用?

Grady Booch 在《面向对象分析与设计》还说这这么一段话

一个对象是一个具有状态、行为和标识符的实体。结构和行为类似的对象定义在他们共同的类中。“实例” 和 “对象” 这两个术语可以互换使用。

也就是说,编程语言中对象具有一下三个特征:

  • 具有唯一标识性:即使是两个完全相同的对象,他们也不是同一个对象。(就像佛陀说的,世界上没有完全相同的两片叶子。或者想一下同卵双胞胎)
  • 有状态:同一个对象可能会处于不同状态下。(比如你现在是坐着还是站着)
  • 有行为:行为可能会改变状态。(比如,你从坐着的状态通过「站起来」这个行为变成站着)

什么是面向对象

知道了什么是对象,那么「面向对象」又是什么鬼?为什么我们还能经常听到「基于对象」这种讲法?

面向对象的英文是 Object-oriented,oriented 在剑桥词典中的解释是

以…为目标的, 以…为导向的…

所以面向对象,就可以理解为以对象作为向导...。那么 OOP 就是以对象作为向导的编程。

所以我认为「基于对象」这个说法更贴切中国人的思维方式。其实虽然名词不一样背后指向的是同一个意思。

JavaScript 是不是 OOP

这个问题就很容易解决了,只要弄清楚 JS 的对象是不是符合「唯一标识」、「状态」、「行为」这三个特性就行。

首先是第一个特征,我们执行下面的代码很容易就能验证:

var o1 = { a: 1 };
var o2 = { a: 1 };
console.log(o1 == o2); // false

至于第二和第三个特征,在不同的编程语言中叫法也不一样,在 C++ 中叫做「成员变量」和「成员函数」,在 Java 中则称为「属性」和「方法」,而在 JavaScript 中统一抽象为「属性」。

var o = { 
    d: 1,
    f() {
        console.log(this.d);
    }    
};

这段代码中, o 是一个对象,d 是一个属性,f 函数也是一个属性,但是它具有「行为」的能力。

综上所述,JavaScript 是正宗的面向对象编程的语言。

JavaScript 为什么没有类

主流的 OOP 语言都支持 class 这个概念,而 JS 一直是用的「原型(prototype)」,直到 ES6 才有支持这个特性(其实 ES6 中的 class 也是一个半吊子)。

其实不管是 class 还是 prototype 都是抽象描述对象的一种方式,它们之间并没有什么优劣之分。

只不过基于 class 的语言中,更强调在不同对象之中抽象出公共的部分组织成 class,再利用 class 去实例化出 object。而基于 prototype 的语言中,更为提倡的是去关注每个对象的行为,之后再去关心将这些对象划分到使用方式相似的「原型对象」中。

最后

OOP 是一种一种编程范式或者说编程风格,它是以对象作为组织代码的基本单元,并且将「封装」、「抽象」、「继承」、「多态」四个特性作为代码设计和实现的基石。

参考:

刘铁锰:C#语言入门详解

Grady Booch:《面向对象分析与设计》

winter:JavaScript对象:面向对象还是基于对象?JavaScript对象:我们真的需要模拟类吗?

王争:04 | 理论一:当谈论面向对象的时候,我们到底在谈论什么?

MDN:JavaScript 对象基础


如果你觉得文章写的还不错,那就动动手指点个赞呗。如果你有不同的想法欢迎在评论区进行交流。