程序员的语言“艳遇史”(五)——办公室秘书Smalltalk
(***以下小说情节纯属虚构,供朋友们在紧张编程后轻松一下,如有雷同纯属巧合,切勿对号入座***)
第五个女孩 办公室秘书Smalltalk
一、引子
每次大学校友聚会,我和二胖都有一爱好,就是找到我们的老班长黑哥,进行一番疯狂的摧残和蹂躏,其惨烈程度,让渣滓洞中美合作所都自愧不如。红酒、白酒加啤酒灌他,稍有不从,我们立马掏出毕业纪念照,众目睽睽下让他无话可说。
要知道为什么有如此深仇大恨,就得追溯到大四时代了。
二、分猪仔的毕业设计
那年头真是风起云涌,家事国事天下事,事事激动人心。对我们计算机系学生来说,头等大事就是操作系统改朝换代了。闷热的暑期,电脑城里热火朝天,小贩扯着喉咙喊,“瘟都死95,新到光盘,数量有限,欲购从速”。开假回校一看,学校里除了计算中心这个最后的UNIX堡垒,其余实验室纷纷沦陷,全部被Windows占领。本人以前一向看不起图形用户界面,觉得效率狂低,认为DOS至少还可以称霸五年。没曾想形势如此急转直下,也只得认栽,开始认真看待Windows编程。
许多单位的MIS系统也开始从DOS向Windows升级,这给我系带毕业设计的教授提供了无限商机。你想想,以组织实习和毕业设计的名义,抓一批比民工还便宜的大学生做工程,真的是一本万利啊!为避免教授们抢生源大打出手,我系一向有“分猪仔”的优良传统。即把毕业班学生按学业水平分几个层次,各层次猪仔在教授们中进行均分,避免挑肥拣瘦的现象。
三、流氓也来需求调研
我、二胖等五人被发配到深圳一个仓储物流行业的国有企业,为其开发MIS系统。我们从火车站出来,四处张望,发现业主单位居然派了个大美女来接站。没错,那就是我们的Smalltalk姐姐。她是项目的接口人,负责安排我们吃住,同时协助搞需求调研。
灿烂的阳光下,我戴着墨镜,后面跟着几条大汉,一路横冲直撞,来到一仓库门口。仔细对了门牌号,便推门进去。只听着里面“啊”一声惨叫,几个仓管都吓得躲到桌子底下了。我们好说歹说,连学生证都掏出来了,说自己是来做需求调研的开发人员。仓管大姐愣是不信,还好Smalltalk姐及时赶到,为我们做了证明。否则事就大了,连110都差点介入。深圳治安乱啊,这仓库前几天才被一伙流氓给劫了。我们一副从校园带来的痞子打扮,仓管把我们误认为劫匪了。难怪刚才路上行人看到我们和见鬼了似的,避之唯恐不及。我还故作亲善的朝一小女孩乐,那孩子莫名其妙就哭了,她妈抱起她就跑,连掉下来的果冻都不要了。
靠!这也太有辱我们名校形象了。在Smalltalk姐的帮助下,我们立即开展了整风运动,规定了公共场合不说粗话、不穿裤衩拖鞋、走路走直线等三大纪律八项注意。违者剥夺看黄书看A片之权利(这点肯定要瞒着Smalltalk姐了)。
四、OOP之胡吃海喝顿悟篇
需求确认以后,开始着手设计开发。大家知道,我一向对C++嗤之以鼻,什么啊,效率比C语言差多了(主要是因为自己没学通)。有个别同志还鼓吹面向对象设计方法什么的,我故作深沉,和他详细剖析了一个MIS项目如何才能省工省力。在我这个组长的淫威下,大家最终一致选择漂亮又轻松的VisualBasic做开发工具。
此后夜里我经常从梦中惊醒,回想起前一段给一些单位投了简历,其中鼓吹自己精通C++,熟练掌握面向对象方法等等,心里坎坷不安。于是潜入书店买了一些参考书,没人的时候独自研读。别在面试时候露馅,脸就丢大了。唉,那时候没什么外版书,又上不了网,国内作者基本属于二道贩子、三道贩子,观点各异,互不买账。不看还好,多看了几本,把我看得一脑门浆糊,原来对OOP还有点直觉,现在更混乱了。
那一段我们经常熬夜,加班加点进行项目开发。Smalltalk姐看在眼里,疼在心里。于是经常带我们下馆子,改善生活。吃在广东,真不是吹的。海鲜大排档和川菜是我们的最爱。在胡吃海喝中,我突然对面向对象方法有了点禅宗顿悟的感觉。
戴上面向对象这副眼镜,我们一起洞察下馆子过程。
我们要去的某餐馆就是一个对象了。坐下以后,店小二送上菜单,这就是此对象的对外服务协议了,每道菜好似对象的一个方法。
“水煮活鱼,四斤的一条!”。随着小二的吆喝,方法的调用开始了,你看,还带参数,四斤的鱼。
至于菜怎么做出来,店家一般不让你知道。人家在封装性这一点上做得多好啊。到底用的料是死鱼还是活鱼,下的油是地沟油还是花生油,泡的椒是回收的还是新鲜的,不得而知。
吃完后,盘子碟子得收了洗洗再用吧,这就算资源回收过程了。有一次我们到某海边竹排上的风味饭馆吃海鲜,只见隔壁座一兄弟吃完饭,把碗飞出一个漂亮的弧线,直接奔海里去了。我们目瞪口呆,后来才知道老板心眼实,按桌面上碗的个数算饭钱。于是个别同志就出此阴招,看看,资源管理出大问题了吧。
假如某家餐馆事业做大了,开始搞加盟店了,什么东街店、西门店,这就算抽象到类的层次了。最典型的例子是麦当劳。但是洋快餐也得入乡随俗,要考虑国情嘛。您到印度麦当劳,点个什么牛肉堡、猪肉堡,小心被人打(这就算侮辱了信仰印度教和伊斯兰教的阿三群众了)。所以要考虑统一性,也要顾及差异性,其他海外地区扩展的麦当劳分支就算美国麦当劳的子类了,属于继承的关系。
五、Smalltalk姐送的喜糖
随着开发过程的不断深入,需求经常更改,事情变得琐碎,我们的士气也日渐低落。也难怪,一群花和尚,整天抓在一起闭关苦修,又不给结算工钱,换谁不疲掉啊!再加上那年头风云激荡的,分心的因素太多了。
先是解放军向东海试射导弹,形势一度紧张,把我们这批好战分子激动得不行,整天在宾馆收看香港电视节目,开发任务也耽误下来了。看看也就罢了,一些同志还搞沙盘推演,尽研究导弹打美军航母了。现在我还后悔,都是人才啊,当时怎么没向总参谋部推荐一下,台湾问题也不会拖到现在。
台海局势才安稳下来,足球迷的狂欢节又到了,欧洲杯开打,接踵而至的是世界杯外围赛。再加上谈恋爱玩失踪的,真都赶上了。进度严重滞后,把我这个项目小组长给气得差点没吐血。最后几个模块草草完工,质量可想而知。
后来软件在使用过程中出了一些问题,业主投诉了。项目小组早已做鸟兽散,都忙着找工作呢。能怎么办,只能我这个组长当冤大头,孤身赴特区处理善后事宜。
同样是在深圳火车站,接站的还是Smalltalk姐,可心境与上一次大不相同了。终于有和梦中仙女单独相处的机会了,我心中窃喜。可没几天美梦就破灭了,我收到了Smalltalk姐送来的一包喜糖。这是哪来的牛粪,把我心中的鲜花给占了。我气得咬牙切齿,后来一打听,公司高干,还开一辆跑车,立马陷入郁闷中……
六、Smalltalk语言的面向对象三法印
在那苦难岁月中,陪伴我的就是一本国防科大出的对象技术导论的书。夜深人静,孤枕难眠,只好翻书瞎琢磨。该书介绍了C++和Smalltalk两种典型的面向对象语言。真是有心栽花花不成,无心插柳柳成荫,我原本打算研究C++的,没想到后来越来越喜欢Smalltalk起来。
施乐公司PARC实验室在上个世纪70年代,研发了Smalltalk语言及Dynabook计算机,该项目不仅是个人计算机的先驱,还创新推出历史上首个成型的图形用户界面(Windows和Mac的老祖宗在这呢!),许多面向对象理念也发源于此(嘿嘿,知道重构是哪里来的了吧?)。如此摧枯拉朽的创新,在计算机科学史上真得不多见。
借用佛家术语,Smalltalk的面向对象特性可总结如下三法印:
- 万物皆为对象。
- 计算即为对象间消息传递。
- 消息与对象方法间均为动态绑定。
拿着三法印,看看如何解构其他程序设计语言中常见的操作,揭露它们毫无自性,皆为虚无。
3+5相当于向3这个对象发送+消息,带5这个参数。
条件选择变成向布尔对象发送ifTrue: ifFalse:消息,比如1 < 2 ifTure: [100] ifFalse: [42],返回值100。
再看循环的例子,(1to: 10) do: [:n| Transcript show: n; cr ],学过Ruby的同志肯定笑了,好相象啊! 这里发生了多次的消息传递。先向1发送to:消息生成了一个interval对象,然后向此对象发送do:消息,带一个代码块做参数(相当于匿名函数或者词法闭包)。连续执行块10次,把步进值作为参数送给块,打印出来。
类也是对象,创建一个对象就是向类发送new消息,注意new不是关键字,只不过是个消息名。创建一个子类呢?向类发送subclass消息,动态产生。那么类的类是什么呢?元类。那么元类的类(这么变态的追问)呢?为了避免无限倒退,Smalltalk语言设置元类的类为Metaclass,而Metaclass的类也是Metaclass自身的实例。
别忘了Smalltalk的语言环境、编译器和垃圾回收器也是一组对象,你可以查看其类定义,然后进行动态修改。就这一点,现在流行的集成开发环境也没几家能做到。退出Smalltalk开发环境时,可以把所有的修改包括新开发的类、对象,全部用image的方式存储起来,下次再用。
真按这书说的,Smalltalk语言也太伟大了!我不禁心潮澎湃。但是哪里搞得到Smalltalk语言工具呢?现在直接上网down一个就行了,当时可没这么方便,于是我又吃饱了撑着想实现一个迷你山寨版的Smalltalk解释器,I do I understand嘛。
一动手,问题来了。我发现面向对象的这些概念离机器层面确实远了点,不象过程语言的函数,一看到它我的脑子里就浮现出堆栈、活动记录这些形象。这些概念也不是来自数学,而是来自现实生活的隐喻,语义上难免模糊。所以实现策略影响很大,甚至反过来重新定义语言。
如果我们把面向对象中的类想象为抽象数据类型,就会得到一种经典的实现。对象=局部环境+类指针,而消息/方法映射表存在类里面。对象收到消息,先通过类指针找到直接类,在它的消息/方法表里进行消息名匹配。如果找不到,顺藤摸瓜到上一级父类中去找。找到对应方法后,把对象本身作为一个隐匿的参数传给方法,方法中用self变量引用它。创建对象同样是一个顺藤摸瓜的过程,必须把整条继承链都巡遍了,把所有类定义中的变量模板合并了,才能做出一个对象。
如果我们重视Smalltalk变量无类型的特征,那么就可以把类实现为一个生成对象的函数。对象=局部环境+ 消息/方法映射表,把方法表放到对象里。创建对象时,不仅要合并继承链上各类中定义的变量模板,连方法表也要合并,形成了一个重量级的对象。创建对象花的时间变长了,可消息动态查找过程效率高了一点。顺着这条路走到黑,把类的概念完全取消,就会得到原型继承模式的面向对象流派(比如javascript之类的语言)。在此模式中,要继承一个对象,先克隆它,然后动态修改其方法表。
上面所说的足以把经典静态类型OOP的信仰者气得跳脚。但还有更猛的异端,让他们绝对吐血,那就是Common Lisp语言的对象系统CLOS。它也自称是面向对象的(细节这里不谈了,后面某篇可能会涉及)。所以说面向对象就象佛教理论,属于术语的密林,门派林立,谁也说服不了谁。
七、尾声
带着淡淡的忧愁,我回到了学校。没想到又发生了一件气人的事。真是倒霉时喝凉水都塞牙,在我奔赴深圳受苦受难期间,班级竟然组织拍了毕业照。我当然缺席了,可是黑哥的可气之处在于,把我和二胖弄混了,照片后面写了我的名字,但对应的是二胖的人。我是有名无相,二胖是有相无名。这班长当的,连兄弟都搞错了!就凭这点,我和二胖还不把黑哥修理得没有人样。靠,见一次揍一次。
1997就这样跌跌撞撞的过去,我毕业了。