第2章是懒人造就了方法 光。 64k的文件是什么概念呢? 1行代码大概(平均)是30字节,64k的源代码是2184 行,如果代码风格好一点,再多一些空行的话,差不多也 就是3000行上下。 也就是说,在Delphi 1的时代(以及其后的很多很多 时代),程序员把3000行代码写到一个文件里,是司空见 惯的事了。如果你不让他这样写,还是会被痛骂的呢。 所以呢,按照这一部分人的逻辑,一百万行代码其实 是可以写在一个文件里的。不单可以,而且编译器、编辑 器等等也都必须要支持。这才是正统的软件开发。 勤快的愚公创造不了方法。这我已经说过了。对于要 把“一百万行代码写到一个文件”,查找一个函数要在编 辑器里按五千次PageDown/,PageUp键的勤快人来说,是 不能指望他们创造出“单元文件(Uit)”这样的开发方法 来的。 然而单元文件毕竞还是出现了。这个世界上,有勤快 人就必然有懒人,有懒人也就必然有懒人的懒方法。 有了单元文件,也就很快出现了一个新的概念:模块。 把一个大模块分成小模块,再把小模块分成更细的小小模 块,一个模块对应于一个单元。于是我们可以开始分工作 -18-
第 2 章 是懒人造就了方法 -18- 光。 64k 的文件是什么概念呢? 1 行代码大概(平均)是 30 字节,64k 的源代码是 2184 行,如果代码风格好一点,再多一些空行的话,差不多也 就是 3000 行上下。 也就是说,在 Delphi 1 的时代(以及其后的很多很多 时代),程序员把 3000 行代码写到一个文件里,是司空见 惯的事了。如果你不让他这样写,还是会被痛骂的呢。 所以呢,按照这一部分人的逻辑,一百万行代码其实 是可以写在一个文件里的。不单可以,而且编译器、编辑 器等等也都必须要支持。这才是正统的软件开发。 勤快的愚公创造不了方法。这我已经说过了。对于要 把“一百万行代码写到一个文件”,查找一个函数要在编 辑器里按五千次 PageDown/PageUp 键的勤快人来说,是 不能指望他们创造出“单元文件(Unit)”这样的开发方法 来的。 然而单元文件毕竟还是出现了。这个世界上,有勤快 人就必然有懒人,有懒人也就必然有懒人的懒方法。 有了单元文件,也就很快出现了一个新的概念:模块。 把一个大模块分成小模块,再把小模块分成更细的小小模 块,一个模块对应于一个单元。于是我们可以开始分工作
「大道至简」 了,一部分人写这几个单元的代码,另一部分则写那几个。 很好,终于可以让源代码分散开来。结构化编程的时 代终于开始了,新的方法取代了旧的方法,而这一切的功 劳,是要归终于那个在按第5001次PageDown键时,突然 崩溃的程序师。他发自良心地说:不能让这一切继续下去 了,我一定要把下一行代码写到第二个文件里去。我发誓, 我要在编译器里加入一个Unit关键字。① 3.你桌上的书是乱的吗? 几周之前,在一所电脑培训学校与学生座谈时,一个 学员问我:“为什么我学了一年的编程,却还是不知道怎 么写程序呢”。 我想了想,问了这个学员一个问题:“你桌上的书是 乱的吗?” 他迟疑了一下,不过还是回答我道:“比较整齐。” 我当时便反问他:“你既然知道如何把书分类、归整 得整整齐齐地放在书桌,那怎么没想过如何把所学的知道 分类一下,归纳一下,整整齐齐地放在脑子里呢?” 如果一个人学了一年的编程,他的脑袋里还是昏乎乎 的,不知道从哪里开始,也不知道如何做程序。那想来只 ①Turbo Pascal3.0中才开始有了Uses和Unit关键字。在ANSI Pascal 标准里并没有它。 -19-
『大道至简』 -19- 了,一部分人写这几个单元的代码,另一部分则写那几个。 很好,终于可以让源代码分散开来。结构化编程的时 代终于开始了,新的方法取代了旧的方法,而这一切的功 劳,是要归终于那个在按第 5001 次PageDown键时,突然 崩溃的程序师。他发自良心地说:不能让这一切继续下去 了,我一定要把下一行代码写到第二个文件里去。我发誓, 我要在编译器里加入一个Unit关键字。① 3. 你桌上的书是乱的吗? 几周之前,在一所电脑培训学校与学生座谈时,一个 学员问我:“为什么我学了一年的编程,却还是不知道怎 么写程序呢”。 我想了想,问了这个学员一个问题:“你桌上的书是 乱的吗?” 他迟疑了一下,不过还是回答我道:“比较整齐。” 我当时便反问他:“你既然知道如何把书分类、归整 得整整齐齐地放在书桌,那怎么没想过如何把所学的知道 分类一下,归纳一下,整整齐齐地放在脑子里呢?” 如果一个人学了一年的编程,他的脑袋里还是昏乎乎 的,不知道从哪里开始,也不知道如何做程序。那想来只 ① Turbo Pascal 3.0 中才开始有了Uses和Unit关键字。在ANSI Pascal 标准里并没有它
第2章是懒人造就了方法 有一个原因:他学了,也把知识学进去了,就是不知道这 些知识是干什么的。或者说,他不知道各种知识都可以用 来做什么。 其实结构化编程的基本单位是“过程(Procedure)”, 而不是上一小节说到的“单元(Unit)”。然而在我看来,过 程及其调用是CPU指令集所提供的执行逻辑,而不是普 通的开发人员在编程实践中所总结和创生的“方法”。 这里要提及到CPU指令集的产生。产生最初的指令集 的方式我已经不可考证,我所知道的是CI$C指令集与 RISC指令集之争在1979年终于爆发。前者被称为复杂指 令集,然而经过Patterson等科学家的研究,发现80%的 CISC指令只有在20%的时间内才会用到;更进一步的研 究发现,在最常用的10条指令中,包含的流程控制只有 “条件分支(IF.THEN.)0”、“跳转(JUMP)”和“调用返 回(CALL/RET)”… 于是CISC被RISC(精简指令集计算机)替代了。动摇 CISC指令集地位的方法,就是分类统计。 正如CISC指令集搅乱了一代程序设计师的思路一 样,大量的知识和资讯搅乱了上面给我提问的那位学员的 思想。他应该尝试一下分类,把既有的知识象桌子上的书 一样整理一下,最常用的放在手边,而最不常用的放在书 ①在x86系统中,循环是用条件分支来实现的,而且条件分支指令 并不是IF..THEN.,这里用这两个关键字,仅用于说明问题。 -20-
第 2 章 是懒人造就了方法 -20- 有一个原因:他学了,也把知识学进去了,就是不知道这 些知识是干什么的。或者说,他不知道各种知识都可以用 来做什么。 其实结构化编程的基本单位是“过程(Procedure)”, 而不是上一小节说到的“单元(Unit)”。然而在我看来,过 程及其调用是 CPU 指令集所提供的执行逻辑,而不是普 通的开发人员在编程实践中所总结和创生的“方法”。 这里要提及到CPU指令集的产生。产生最初的指令集 的方式我已经不可考证,我所知道的是CISC指令集与 RISC指令集之争在 1979 年终于爆发。前者被称为复杂指 令集,然而经过Patterson等科学家的研究,发现 80%的 CISC指令只有在 20%的时间内才会用到;更进一步的研 究发现,在最常用的 10 条指令中,包含的流程控制只有 “条件分支(IF...THEN...) ①”、“跳转(JUMP)” 和“调用返 回(CALL/RET)”…… 于是 CISC 被 RISC(精简指令集计算机)替代了。动摇 CISC 指令集地位的方法,就是分类统计。 正如 CISC 指令集搅乱了一代程序设计师的思路一 样,大量的知识和资讯搅乱了上面给我提问的那位学员的 思想。他应该尝试一下分类,把既有的知识象桌子上的书 一样整理一下,最常用的放在手边,而最不常用的放在书 ① 在x86 系统中,循环是用条件分支来实现的,而且条件分支指令 并不是IF...THEN...,这里用这两个关键字,仅用于说明问题
「大道至简」 柜里。如果这样的话,我想他己经在九个月前就开始写第 一个软件产品了。 你桌上的书还是乱的吗? 4.我的第一次思考:程序=算法+结构+方法 我的第一次关于程序的本质的思考其实发生在不久 前。那是我在OICQ上与Soul的一次谈话。 Soul(王昊)是DelphiBBS现任的总版主,是我很敬重的 一位程序员。那时我们正在做DelphiBBS的一个“B计划 Ⅱ”,也就是出第二本书。他当时在写一篇有关“面向对 象(OOP)”的文章。而我正在写《Delphi源代码分析》,在 初期的版本里,有“面向对象”这一部分的内容。我们的 对话摘要如下①: Soul:我在写书讨论“面向对象的局限性” 我:En.这个倒与我的意见一致。哈哈哈。 我:“绝对可以用面向过程的方法来实现任意复杂的系统。要知道,航 天飞机也是在面向过程的时代上的天。但是,为了使一切变得不是 那么复杂,还是出现了‘面向对象程序设计'的方法。 我 :一一哈,我那本书里,在“面向对象”一部分前的引文中。就是这 样写的。 ①这段对话的确很长。如果你不是非常有经验的程序员,那么不 能完整地阅读和理解这段文字是很正常的。部分读者甚至可以跳 过这段引文,直接阅读后面的结论。而有兴趣的读者,可以在我 的网站上读到它的全文(http:/www.doany.net/)。 -21-
『大道至简』 -21- 柜里。如果这样的话,我想他已经在九个月前就开始写第 一个软件产品了。 你桌上的书还是乱的吗? 4. 我的第一次思考:程序 = 算法 + 结构 + 方法 我的第一次关于程序的本质的思考其实发生在不久 前。那是我在 OICQ 上与 Soul 的一次谈话。 Soul(王昊)是DelphiBBS现任的总版主,是我很敬重的 一位程序员。那时我们正在做DelphiBBS的一个“B计划 II”,也就是出第二本书。他当时在写一篇有关“面向对 象(OOP)”的文章。而我正在写《Delphi源代码分析》,在 初期的版本里,有“面向对象”这一部分的内容。我们的 对话摘要如下①: Soul:我在写书讨论“面向对象的局限性” 我 :En.这个倒与我的意见一致。哈哈哈。 我 :“绝对可以用面向过程的方法来实现任意复杂的系统。要知道,航 天飞机也是在面向过程的时代上的天。但是,为了使一切变得不是 那么复杂,还是出现了‘面向对象程序设计’的方法。” 我 :——哈,我那本书里,在“面向对象”一部分前的引文中。就是这 样写的。 ① 这段对话的确很长。如果你不是非常有经验的程序员,那么不 能完整地阅读和理解这段文字是很正常的。部分读者甚至可以跳 过这段引文,直接阅读后面的结论。而有兴趣的读者,可以在我 的网站上读到它的全文(http://www.doany.net/)
第2章是懒人造就了方法 Sou1:现在的程序是按照冯。诺伊曼的第一种方案做的,本来就是顺序的, 而不是同步的。CPU怎么说都是一条指令一条指令执行的。 我:面向过程是对“流程”、“结构”和“编程方法”的高度概括。而 面向对象本身只解决了“结构”和“编程方法”的问题,而并没有 对“流程”加以改造。 Sou1:确实如此。确实如此。 我:对流程进一步概括的,是“事件驱动”程序模型。而这个模型不是 OO提出的,而是Windows的消息系统内置的。所以,现在很多人迷 惑于“对象”和“事件”,试图通过00来解决一切的想法原本就是 很可笑的。 Sou1:我先停下来,和你讨论这个问题,顺便补充到书里去。 我:如果要了解事件驱动的本质,就应该追溯到Windows内核。这样就 涉及到线程、进程和窗体消息系统这些与O0无关的内容。所以,整 个RD的编程模型是O0与OS一起构建的。现在很多的开发人员只知 其0O的外表,而看不到0S的内核,所以也就总是难以提高。 Sou1:00里面我觉得事件的概念是很牵强的,因为真正的对象之间是相互 作用,就好像作用力和反作用力,不会有个“顺序”的延时。 我:应该留意到,整个的“事件”模型都是以“记录”和“消息”的方 式来传递的。也就是说,事件模型停留在“面向过程”编程时代使 用的“数据结构”的层面上。因此,也就不难明白,使用/不使用 OO都能写Windowsi程序。 我:因为流程还是在“面向过程”时代。 Sou1:所以所谓的面向对象的事件还是“顺序”的。所以我们经常要考虑 一个事件发生后对其他过程的影响,所以面向对象现在而言是牵强 的。 我:如果你深入OS来看SEH,来看Messages,就知道这些东西原本就不 是为了“面向对象”而准备的。面向对象封装了这些,却无法改造 它们的流程和内核。因为00的抽象层面并不是这个。 我:事件的连续性并不是某种编程方法或者程序逻辑结构所决定的。正 如你前面所说的,那是CPU决定的事。 Sou1:比如条件选择,其实也可以用一种对象来实现,而事实没有。这个 是因为cpu的特性和面向对象太麻烦。 我:可能,将CU做成面向对象的可能还是比较难于想象和理解。所以 MS才启动.NET Framework。我不认为.NET在面向对象方法上有什么 -22-
第 2 章 是懒人造就了方法 -22- Soul:现在的程序是按照冯。诺伊曼的第一种方案做的,本来就是顺序的, 而不是同步的。CPU怎么说都是一条指令一条指令执行的。 我 :面向过程是对“流程”、“结构”和“编程方法”的高度概括。而 面向对象本身只解决了“结构”和“编程方法”的问题,而并没有 对“流程”加以改造。 Soul:确实如此。确实如此。 我 :对流程进一步概括的,是“事件驱动”程序模型。而这个模型不是 OO提出的,而是Windows的消息系统内置的。所以,现在很多人迷 惑于“对象”和“事件”,试图通过OO来解决一切的想法原本就是 很可笑的。 Soul:我先停下来,和你讨论这个问题,顺便补充到书里去。 我 :如果要了解事件驱动的本质,就应该追溯到Windows内核。这样就 涉及到线程、进程和窗体消息系统这些与OO无关的内容。所以,整 个RAD的编程模型是OO与OS一起构建的。现在很多的开发人员只知 其OO的外表,而看不到OS的内核,所以也就总是难以提高。 Soul:OO里面我觉得事件的概念是很牵强的,因为真正的对象之间是相互 作用,就好像作用力和反作用力,不会有个“顺序”的延时。 我 :应该留意到,整个的“事件”模型都是以“记录”和“消息”的方 式来传递的。也就是说,事件模型停留在“面向过程”编程时代使 用的“数据结构”的层面上。因此,也就不难明白,使用/不使用 OO都能写Windows程序。 我 :因为流程还是在“面向过程”时代。 Soul:所以所谓的面向对象的事件还是“顺序”的。所以我们经常要考虑 一个事件发生后对其他过程的影响,所以面向对象现在而言是牵强 的。 我 :如果你深入OS来看SEH,来看Messages,就知道这些东西原本就不 是为了“面向对象”而准备的。面向对象封装了这些,却无法改造 它们的流程和内核。因为OO的抽象层面并不是这个。 我 :事件的连续性并不是某种编程方法或者程序逻辑结构所决定的。正 如你前面所说的,那是CPU决定的事。 Soul:比如条件选择,其实也可以用一种对象来实现,而事实没有。这个 是因为cpu的特性和面向对象太麻烦。 我 :可能,将CPU做成面向对象的可能还是比较难于想象和理解。所以 MS才启动.NET Framework。我不认为.NET在面向对象方法上有什么