1.从机器码到面向对象
本章节主要探讨是什么驱动着编程从机器码发展到了汇编语言,又从汇编语言发展到了面向过程编程,最后从面向过程编程发展到面向对象编程。通过这些探讨最终明确多年来的软件工程发展我们都解决了哪些棘手的问题。
1.1机器码
在真正的电子计算机诞生初期,没有有效的编程语言。在这个时期的计算机提供了最基础的由0和1组成的机器指令来供初代的程序员进行编码。
用机器码写的程序如示例-1-1所示。
101100000000000000000011
000001010000000000110000
001011010000000000000101
示例-1-1 8086机器上完成“s=768+12288-1280”的数字运算,对应的机器码
有了示例-1-1所示的机器码指令后,会将指令编排到打孔纸带上,输入到计算机中。如示例-1-2所示。计算机持续读取指点上的指令来完成任务。
示例1-2 打孔纸带示意图(可以假设有孔为1,无孔为0)
可以想象在通过这种方式进行编程场景大致会是这样——手里拿着指令手册来查询指令所对应的机器码,然后对纸带按照指定进行打孔。在计算机应用的早期,计算机所处理的任务多为简单的数学问题,也就是说当时的程序员所面临的问题复杂度在当时看来并不复杂。所以程序员可以快乐地遇到一个问题就打孔一个纸带然后输入到计算机进行求解。
但是好景不长,随着计算机的发展,计算机所能处理能力越来越复杂,指令集也成百上千地增加,同时程序也越来越长。这时程序员第一次感觉到这些又臭又长,一眼看不到头的机器码维护起来是那么令人抓狂。这时的程序员所渴望的就是一种有自然语义的助记符来帮助自己进行编程,汇编语言应运而生。
1.2汇编语言
汇编语言被称为第二代计算机语言,用一些容易理解和记忆的缩写单词来代替一些特定的指令。通过这种方法,人们很容易去阅读已经完成的程序或者理解程序正在执行的功能,对现有程序的bug修复以及运营维护都变得更加简单方便。但计算机的硬件不认识字母符号,这时候就需要一个专门的程序把这些字符变成计算机能够识别的二进制数或机器语言(汇编编译器)。
用汇编语言编写代码如示例-1-3所示。
mov ax, 300h
add ax, 3000h
sub ax, 500h
示例-1-3 8086机器上完成“s=768+12288-1280”的数字运算,对应的汇编指令
可以看到示例-1-3所示的程序比起示例-1-1来简单了很多,并且每条指令都是一个人能读懂的指令。这样编写和维护起来就轻松了很多。但是汇编语言还是存在一定的缺陷——程序员依然在面向硬件编程。也就是说我们在编写程序的时候不是在编写我们要解决的问题,而是要将问题翻译成一堆硬件相关的操作。这就要求程序员非常了解底层的硬件结构。为了屏蔽底层的硬件复杂性,前辈们提出了高级语言。
1.3高级语言
最初的高级语言代表有Fortran、LISP、Cobol等,不过影响最深刻的应该是C语言。有了这些高级语言后程序员不需要关注机器底层的低级结构和逻辑,而只要关注具体的问题和业务即可。
用C语言编写代码如示例-1-4所示。
int s=768+12288-1280;
示例-1-4 使用C语言完成“s=768+12288-1280”的数字运算
在拥有了高级编程语言后,程序员的第一个春天到来了。由于高级语言对底层硬件的屏蔽,让编程的门槛大幅降低。越来越多的人参与到程序编写,越来越多的业务也通过编程软件来进行实现。但好景不长,随着软件的规模和复杂度的大大增加,20世纪60年代中期开始爆发了第一次软件危机,典型表现有软件质量低下、项目无法如期完成、项目严重超支等,因为软件而导致的重大事故时有发生。
之所以发生这样的危机,究其根源便是软件编码过于随意,缺乏有效的代码组织和模块划分。为了解决这次危机面向过程(结构化程序设计)的编程思想被正式提出。
1.4面向过程编程
面向过程编程的核心思想是“自顶向下、逐步细化、模块化”。在这种思想的指导下,面向过程编程以过程(可以理解为方法、函数、操作)作为组织代码的基本单元。面向过程风格是一种流程化的编程风格,通过拼接一组顺序执行的方法来操作数据完成一项功能。
在这种指导思想的基础上,程序员首先会用流程图分析出解决问题的每一个步骤,然后使用函数将每一个步骤进行实现,最后使用条件判断和循环将这些函数组织在一起来实现业务逻辑。在这个过程中程序员将大段代码拆分成了一个个独立的函数,最终得到的就是层层调用的函数,就可以将复杂的业务分解成简单的任务。这种方式以函数为单元,将相关业务紧密内聚在函数内部,最终达到高内聚低耦合的目标。
有了一个又一个的函数来封装重复的功能,代码的可维护性和重用能力都大大提高了。
这时程序员在面对复杂的业务逻辑时已经可以进行很好的应对了。但是随着软件行业的发展越来越迅速,需求变化也就越来越频繁。为了适应各种场景,面向过程的编码往往会存在着大量的条件判断语句——大体就是条件1该走设么逻辑,条件2该走什么逻辑,条件3等等。随着这些特殊逻辑的增加,本身清晰的代码逻辑不再清晰,新增一个条件判断是要在很多地方进行修改等等让软件变得越来越难维护的噩梦开始出现。最终导致软件的声明周期变短——重新开发。
其根本原因来自于面向过程编程在应对软件扩展时缺乏有效的手段。所以面向对象编程便呼之欲出了。
1.5面向对象编程
为了能够更好地应对需求变化所带来的软件开发复杂性,构建具有扩展性的代码的需求越来越迫切。而面向对象编程似乎就成了仙丹妙药。通过以“封装”、“继承”和“多态”三大基础特性为基础,以“单一原则”、“开闭原则”和“里氏替换"等原则性的定理为发展,最后诞生了设计模式以及完整的代码重构理论这些过程的不断完善,程序员可以基于面向对象的编程来完成现代软件开发中的各种复杂问题。
基于面向对象的编程思想来进行软件设计产出的结果就是一个一个的拥有属性和行为的对象之间的相互调用。并且这些对象都是所谓的“充血模型”。
1.6总结
从机器码到面向对象编程的过程中,以引入了一层一层的中间层作为代价,最终得到了一种以“对象”为核心的抽象编程模式。这种抽象编程模式符合人类对自然的抽象——我们会描述一个人有眼睛、鼻子等,这些都是人的属性;描述这个人可以走路、说话这些都是人的行为。在面向对象产生的运行代码中,就仿佛有这么一群拥有自己特有属性和行为的“人”在相互协作来对外提供一个完整的系统功能。