所有编程语言都是一种抽象。甚至可以说,我们能够解决的问题的复杂程度直接取决于抽象的类型和质量。这里提到的“类型”的含义是“你要抽象的是什么”。比如,汇编语言是对计算机底层的一个极简化的抽象。还有许多所谓的命令式编程语言(比如FORTRAN、BASIC和C语言等)都是各自对汇编语言的抽象。虽然这些语言已经取得了长足的进步,但它们主要的抽象方式依然要求你根据计算机的结构而非问题的结构来思考。于是,程序员必须在机器模型(也叫作“解决方案空间”,即实际解决问题的方式,比如计算机)和实际解决的问题模型(也叫作“问题空间”,即问题实际存在之处,比如来源于某个业务)之间建立关联。建立这种关联需要耗费很大的精力,而且它是与编程语言无关的,这一切都导致程序难以编写且不易维护。
构建机器模型的一种代替方案是针对需要解决的问题构建问题模型。早期的一些编程语言(比如LISP和APL)会采取特定的视角看待周遭问题(例如,“所有问题最终都可以用列表呈现”或者“所有问题都是算法问题”),Prolog语言则会将所有问题都转换为决策链。这些语言要么是基于约束性的编程语言,要么是专门用来操作图形符号的编程语言。这些编程语言都能够出色地解决一些特定的问题,因为它们正是为此而生的。然而,一旦遇到它们专属领域以外的问题,它们就显得无能为力了。
面向对象编程则更进一步,它为程序员提供了一些能够呈现问题空间元素的工具。这种呈现方式具备足够的通用性,使得程序员不再局限于特定的问题。而这些问题空间中的元素及其解决方案空间中的具体呈现,我们称其为“对象”(需要注意的是,有些对象并不支持问题空间的类比)。其背后的理念则是,通过添加各种新的对象,程序可以将自己改编为一种描述问题的语言。于是,你阅读的既是解决方案的代码,也是表述问题的文字。这种灵活且强大的语言抽象能力是前所未有的。因此,面向对象编程描述问题的依据是实际的问题,而非用于执行解决方案的计算机。不过,它们之间依然存在联系,这是因为从某种意义上来说,对象也类似于一台小型计算机——每一个对象都具有状态,并且可以执行一些特定的操作。这一特点与现实中的事物极为相似,它们都具有各自的行为和特征。
SmallTalk是历史上第一门获得成功的面向对象语言,并且为后续出现的Java语言提供了灵感。Alan Kay总结了SmallTalk语言的5个基本特征,这些特征代表了纯粹的面向对象编程的方式。
1.万物皆对象。 你可以把对象想象为一种神奇的变量,它可以存储数据,同时你可以“发出请求”,让它执行一些操作。对于你想要解决的问题中的任何元素,你都可以在程序中用对象来呈现(比如狗、建筑、服务等)。
2.一段程序实际上就是多个对象通过发送消息来通知彼此要干什么。 当你向一个对象“发送消息”时,实际情况是你发送了一个请求去调用该对象的某个方法。
3.从内存角度而言,每一个对象都是由其他更为基础的对象组成的。 换句话说,通过将现有的几个对象打包在一起,你就创建了一种新的对象。这种做法展现了对象的简单性,同时隐藏了程序的复杂性。
4.每一个对象都有类型。 具体而言,每一个对象都是通过某个 类 生成的 实例 ,这里说的“类”就(几乎)等同于“类型”。一个类最为显著的特性是“你可以发送什么消息给它”。
5.同一类型的对象可以接收相同的消息。 稍后你就会意识到这句话的丰富含义。举例来说,因为一个“圆形”对象同样也是一个“形状”对象,所以“圆形”也可以接收“形状”类型的消息。这就意味着,你为“形状”对象编写的代码自然可以适用于任何的“形状”子类对象。这种可替换性是面向对象编程的一个基石。
Grady Booch对对象做了一种更为简洁的描述:
对象具有状态、行为及标识。
这意味着对象可以拥有属于自己的内部数据(赋予其状态)、方法(用于产生行为),同时每一个对象都有别于其他对象。也就是说,每一个对象在内存中都有唯一的地址。