我的第一次思考:程序 = 算法 + 结构 + 方法
我的第一次关于程序的本质的思考其实发生在不久前。那是我在OICQ上与Soul的一次谈话。
Soul(王昊)是DelphiBBS现任的总版主,是我很敬重的一位程序员。那时我们正在做DelphiBBS的一个“B计划II”,也就是出第二本书。他当时在写一篇有关“面向对象(OOP)”的文章。而我正在写《Delphi源代码分析》,在初期的版本里,有“面向对象”这一部分的内容。我们的对话摘要如下:
Soul:我在写书讨论“面向对象的局限性”。
我 :En.这个倒与我的意见一致。哈哈哈。
我 :“绝对可以用面向过程的方法来实现任意复杂的系统。要知道,航天飞机也是在面向过程的时代上的天。但是,为了使一切变得不是那么复杂,还是出现了‘面向对象程序设计’的方法。”
我 :——哈,我那本书里,在“面向对象”一部分前的引文中。就是这样写的。
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在面向对象方法上有什么超越,也不认为它的FCL库会有什么奇特的地方。——除了它们足够庞大。但是我认为,如果有一天OS也是用.NET Framework来编写的,OS一级的消息系统、异常机制、线程机制等等都是.NET的,都是面向对象的。那么,在这个基础上,将“事件驱动”并入OO层面的模型,才有可能。
Soul:所以我发觉面向对象的思维第一不可能彻底,第二只能用在总体分析层上。在很多时候,实质上我们只是把一个顺序的流程折叠成对象。
我 :倒也不是不可能彻底。有绝对OO的模型,这样的模型我见过。哈哈~~但说实在的,我觉得小应用用“绝对OO”的方式来编写,有失“应用”的本意。我们做东西只是要“用”,而不是研究它用的是什么模型。所以,“Hello World”也用OO方式实现,原本就只是出现在教科书中的Sample罢了。哈哈。
Soul:还有不可能用彻底的面向对象方法来表达世界。 因为这个世界不是面向对象的。 是关系网络图,面向对象只是树,只能片面的表达世界。所以很多时候面向对象去解决问题会非常痛苦。所以编程退到数据结构更合理,哈哈。
我 :如果内存是“层状存取”的,那么我们的“数据结构”就可以基于多层来形成“多层数据结构”体系。如果内存是“树状存取”的,那么我们当然可以用“树”的方式来存取。——可惜我们只有顺序存取的内存。
我 :程序=数据+算法 ——这个是面向过程时代的事。 程序=数据+算法+方法 ——在OO时代,我们看到了事件驱动和模型驱动,所以出现了“方法”问题。
Soul:我的经验是:总体结构->面向对象,关系->数据结构,实现->算法
Soul:看来我们对面向对象的认识还是比较一致的。
我第一次提到我对程序的理解是“程序=数据+算法+方法”,便是在这一次与Soul的交谈之中。在这次的交谈中的思考仍有些不成熟的地方,例如我完全忽略了在面向过程时代的“方法”问题。实际上面向过程开发也是有相关的“方法”的。
所谓“面向过程开发”,其实是对“结构化程序设计”在代码阶段的一个习惯性的说法。而我忽略了这个阶段的“方法”的根本原因,是即使没有任何“方法”的存在,只需要有了“单元(Unit)”和“模块(Module)”的概念,在面向过程时代,一样可以做出任意大型的程序。在那个时代,“方法”问题并不会象鼻子一样凸显在每一个程序员的面前。
面向过程开发中,“过程(procedure)”是CPU提供的,“单元(unit)”则是编译器提供的(机制)。程序员不需要(至少是不必须)再造就什么“方法”,就可以进行愚公式的开发工作了。
如果不出现面向对象的话,这样伟大的工程可能还要再干一百年⋯⋯
而与“面向对象”是否出现完全无关的一个东西,却因为“过程”和“单元”的出现而出现了。这就是“工程(engineering)”。