静思录 程序第一
知止而后有定,定而后能静,静而后能安,安而后能虑,虑而后能得。 ——《大学》
从写第一个x86程序算起,到现在,转眼也已有十年。变量、数组、指针、引用、函数、命名空间、封装、继承、多态、GP、接口、元数据、反射、FP、DRY、TDD、SOA、WF、LINQ、AOP、DI、LOP ...... 在一条充满了无数HelloWorld、被抛弃的想法、寒夜孤灯以及工程实践"血泪史"的道路上,我从一个同学年少变成了奔三程序员"大叔",并且依然义无反顾地向前狂奔着。直到刚刚那一刻,我才突然回过头,望着那条已经不太能够看得清起点的来时道路,心生一念:是否应该先停一停,作些不同以往的思考?我为何而来?要向何处去?
什么是写程序?为什么写程序的过程是这样的?这样写程序是正确的吗?这样写程序是唯一正确的方式吗?现在,我的工作生活中很大一部分时间都是在写程序;在可以预计的将来,我也不太可能依靠开洗浴中心来讨生活。所以我认为认真思考这些问题对我的人生应该是很有帮助的,而对于那些在类似的道路上前行的朋友们,即便不能起到抛砖引玉的作用、达到无心插柳的效果,至少也可以添作饭后茶余一笑。
好!那么就先停一停,让我们的视线穿过纷繁芜杂的名词,回到那个一切开始的地方——程序。
按照编程趣味读物的惯例写法,时间应该回到19XX年的一个夏夜,年轻的我正为了某个重要的理由在某某平台上用某某开发工具写一个叫某某某的程序,接下来就会若有所思地感慨过去2009减19XX年间世道变了许多。但是,事实上我当时正在用VB6为一个同班漂亮女生写着很蹩脚的五子棋游戏(带有“很炫”的splash screen),这个例子显然不够沧桑与深刻,所以让我们跳过感慨直入主题。
牛人说:人类文明运行于软件之上。窃以为:写程序便是要把人类的意识固化到计算机当中,让这些文明与智慧能够反反复复地运行下去。程序其实并不是在描述客观世界,而是在描述人们对客观世界的认识。如果意识是物质的模型,那么程序就是意识的模型。也许这种二阶模型的构造、变更与验证存在着固有的复杂性——写程序或者更具体地对我而言,开发与维护企业软件系统相对于吃泡面来说是一件很困难的事情。对于绝大多数困难的事情,不同领域的天才们都曾经给出惊人相似的解决方案——分治复用。面对强大的敌人,我们首先要保存自己,接着忽悠最广大人民群众并寻找革命同志,然后奋力挖墙脚,进而剪除顽固不化的死忠,断水断电断煤气,最后直捣黄龙,这就叫分治;前事不忘,后事之师,面对频繁的需求变更与一个接一个崭新的项目,如何能够避免每次都"重新制造车轮",尽量利用已有的开发成果并且使当前的开发成果能够被今后的项目利用,这就是复用。分治是复用的先决条件,复用是分治的最终目标。我所能想起的最早的复用,应该就是 jmp 了。没错,是个汇编指令。虽然这个指令也用来构造条件分支,但是开发人员确实由此获得了免于重复输入相同代码的能力,只要在适当的时候重新跳回起始地址就好了。此后,goto、do-while、foreach、函数、封装、继承、Component、RPC、WebService ... 使得我们可以穿透各种系统边界在不同的层级上复用已经存在的实现。同时我们还应该注意到实现复用之外的另一条主线:接口复用。封装、多态、接口、契约、依赖注入等等技术让我们能够在系统中引入更多的抽象,使得高层代码能够独立于那些可能会发生变化的具体实现,始终保持不变。当我们怀着庖丁解牛的理想,以复用为最终目的,使用分而治之的策略将系统拆分成很多足够细小的抽象概念与具体实现步骤时,一个问题产生了:是什么把这些层层叠叠的部件装配到一起构成了整个系统?换言之,程序代码到底是什么?答案可以高度概括为一个正则表达式——[_A-Za-z][_A-Za-z0-9]*,没错,开发人员使用标识符给系统的任何一部分起名字,实例、属性、方法、事件、类、接口、命名空间、JNDI、DI id ... 在排除了那些流程控制语句和元素声明的写法等等在同一个程序语言中固定不变的内容之后我们发现,任何程序,无论采用何种编程范式,都是由一堆按照固定顺序和方式出现标识符组成的,其中每一个标识符都代表了一个内存地址或者另外一堆标识符。采用不同的编程范式似乎只是在影响高层标识符的意义与组织方式。排除接口复用方面的考虑不谈,如果OB或者OO相对于过程式编程而言还存在其他优势的话,那么主要就体现在前者提供了构造良好分治结构的可能性,封装、继承、接口等技术就像一个黄页分类系统,如果设计良好可以让用户快速定位目标;而过程式编程的开发成果永远是扁平的。 随着系统复杂程度的增加,OO必然也会遇到相同的问题,即如何有效的组织系统的各个部分,让开发人员可以更容易地根据需求进行代码定位,使得所有关于复用的努力能够真正实现;而不是因为引入了过多的抽象层次让系统变得难以理解,最终导致开发人员明明知道可以复用的实现就在这里,却因为陡峭的学习曲线而不得不再次重新制造车轮。
一段典型的现代OO代码片段是这样的:
OO code
这说明我们每天写程序的人的生活也是这样的:拿到一个需求;自己琢磨、Google、MSDN、API Doc、Community或者问一圈同事才找到一些对象;逐一弄清楚上边那些所有的What、WhatYet 以及 WhatElse 都代表了些虾灭;编译测试调试编译测试调试编译测试调试;直到我们真正的弄清楚了那些所有的What、WhatYet 以及 WhatElse 都代表了些虾灭 ...... 每个公司的开发流程可能不同,但是具体到代码签出之后到签入之前的这段繁复的手工操作我想应该还是大同小异的。即使这个框架是你一手设计的,你可以完全理解所有的抽象概念,那么对于一个新加入团队的员工呢?对于十年之后已经不太记得当初那些精心安排的工巧的你呢?
就在这里停下吧!难道我许多年来倾注了满腔的热情,遵循着分治复用的原则,接受了诸多概念、技术、信仰与习惯,最后获得的日常生活就是这样的重复查找与验证么?我本可以用来记忆更多家人朋友消息或是八卦娱乐新闻的大脑存储空间必须要被征用过来,把那些晦涩的标识符及其出现顺序倒背如流么?!
不!我相信生活不应该是这样的。我相信写程序的最终目的应当是为了终结写程序;在OO之外,在现有的编程范式、模式与最佳实践之外一定还有其他更好的办法能够提高程序代码的生产效率、进一步解放千千万万像我一样的人——程序员。
(待续)