首页
社区
课程
招聘
[原创]命运的代码(长篇连载)
发表于: 2009-10-18 10:32 66478

[原创]命运的代码(长篇连载)

2009-10-18 10:32
66478
收藏
免费 7
支持
分享
最新回复 (118)
雪    币: 350
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
76
终于又更新了
2010-2-12 03:10
0
雪    币: 201
活跃值: (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
77
楼主用心了,偶也要用心看看。
2010-2-12 14:34
0
雪    币: 244
活跃值: (20)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
78
看到精彩处,发现又要等待更新了:)

楼主春节愉快!
2010-2-12 17:26
0
雪    币: 558
活跃值: (46)
能力值: ( LV2,RANK:16 )
在线值:
发帖
回帖
粉丝
79
mark,..
2010-2-15 16:20
0
雪    币: 358
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
80
我现在开看 不晚吧
2010-3-5 14:21
0
雪    币: 124
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
81
 虽然没有任何证据,我却直接开始写代码来解决这个问题。这要在现在,显得真是不可思议。因为我解决的问题的计划,是要写大量的代码。而一般这种要动手编码的情况,到了稍微正式一点的大公司里,都是要层层讨论、逐级批准才行的。
 那时候劳动力可真是廉价啊,说干就干了。
 既然我怀疑是通过TCP通信的时候可能再次要求分配内存,我当然就不要再通过TCP协议层层转包了,最好是能操作网卡直接“出口”。页面交换的特点是每次都是一个页面,所以其实根本就没有必要用TCP协议这样。我自己写了一个超级简单的网络协议驱动,直接发送以太网包。其实这事风险很大。如果网卡驱动在发送以太网包的时候还要分配内存,此事又是黄粱一梦。
 服务器是UNIX的。叫做XXBSD系统。我之前从来都没有见过。我必须得能接收到以太网包才行。如果换了以前我会一筹莫展,但是当我进入了这么一个存在牛人的公司的时候,这些问题就已经不是难事了。我问张老大如何在这种BSD的系统上直接截获以太网包,他给了我一段示例的代码。我把它修改成了一个服务程序。能接受以太网包,并维护一个分页交换文件的读写。
 这些程序的调通并不容易。我没有修改原始的虚拟磁盘的驱动,而是新写了一个。用这个来生成了一个新的硬盘。然后通过修改Windows的设置,让Windows指定分页交换文件生成在这个新的网络硬盘上。这时候,差不多一个多月的时间已经过去了。万一要是不行,那可真是只有羞愤自尽了。
 结局出乎我的意料,开到十几个搜狐的时候,依然是照样死机,甚至连一点好转的迹象都没有。
 当我的测试机再度死机的时候,我的大脑几乎也一起死机了。完蛋了。这一个月来的努力恐怕都全部白费了。
 所谓“一分汗水,一分收获”的说法,在职场恐怕是有些说不通。至少汗水和收获的比例,绝对不是线性增长的。就像一万米长跑,如果已经跑了九千九百九十九米,还没到终点就晕倒了,那么,这前面这九千九百九十九米,和根本没动身的价值就是完全一样的。当然,更糟糕的是,跑完了九千九百九十九米,才发现方向完全不对。这是非常可怕和郁闷,但是又是再常见不过的事情。
 如果现在要有人问我,碰到这样的情况应该怎么办,我可真是无言以对。我真的不知道应该怎么办。我一贯的处理,就是坚定的相信自己一定是对的,自己一定不会选择了错误的道路:我坚持走下去,一定可以成功。这就像即使南辕北辙,只要地球是圆的,也一定可以抵达目的地,只不过绕路一点。而在工作的场合,别人唯一关心的是,你是否解决了问题,是否取得了成果。至于你是用绕道地球的方式得到的,还是用聪明巧妙的方式得到的,有谁会关心呢?
 我本以为张老大会时时关注我的进展,或许至少告诉我,我应该在什么时间之前搞定这个问题。但是他整天忙于编程,根本就没空理我。这个任务没有时间限制,这属于那种突然死亡型的工作:如果别人已经解决了这个问题,或者领导认为有更有价值的工作交给我去做,我这个任务也就会被突然宣告失败了。

 我别无选择,决定神挡杀神,佛挡杀佛的做下去。我坚定的认为是因为发送以太网包的一系列动作中,Windows的内核分配了内存。因为内存已经没有了的时候,这样分配内存显然不一定能成功。要成功的办法,就是开一个“绿色通道”。我预先准备一部分不可分页的内存空间,仅仅可供完成分页交换文件访问的过程。保证即使内存全面枯竭,分页交换文件依然可以正常访问。
 这必须重写内存分配。因为内存分配不再从系统堆中分配了,而从我自己准备的一小块空间里分配。内存分配算法是很有的东西,但是我可没那么多空折腾这个,直接写一个最简单的,512字节一块,每次按块分配。每次都寻找没有被分配掉的块即可。
 当然更重要的是要让那些有嫌疑的驱动分配内存的时候,不再调用Windows提供的系统调用,而老实的使用我提供的函数。这个是我自己都觉得最不可思议的一件事:因为我没有Windows的代码,我不可能把那些内核驱动重写一遍。不过那时候,Windows还不保护内核驱动,没有什么“系统文件还原”的机制。所以那些文件可以随便替换,这给我带来了希望。
 那个时代的系统调用都是先往寄存器里保存一个调用编号,然后就调用中断80。分配内存的系统调用的ID是可以查到的:随便找个有分配内存的驱动,用反汇编工具看一下即可。
 既然能看到这个编号,当然就随便替换了。我自己写一个系统调用,分配一个新的ID,替换原有的即可。这需要直接改写内核驱动的二进制文件。这看似很邪恶。
 首先被修理的是NDIS.sys,这是一个至关重要的网络驱动。其次就是网卡的驱动。大部分是vxd文件,还有一部分是sys、drv文件。
 最后找来找去,一些看似无关的驱动也被找出来替换了。比如主管时钟相关的time.drv。
 结果这些驱动分配内存的时候,果然真的使用我自己编写的内存分配函数了。而且Windows居然还可以正常的运行。这真是令我意外惊喜的一件事。而且更令人疯狂的是,在这样疯狂的加工之后,我不断的打开搜狐,到第十三个,没死机了。
 第十四个的时候,菜单字体变成大号了。
 再接着开,Windows开始连窗口都变成空白:内存绝对是极度的枯竭了。但是Windows还是没有死机!
 我小心的点这些窗口,一个一个的关闭掉。我极其小心的挪动鼠标,生怕一个小小的震动,导致死机再次发生。毕竟这个月里,我已经看它死了无数次了。
 关闭之后,Windows又恢复了原状,看似很稳定的样子。我又仔细的检查了一遍各种配置和文件,确定我不是头脑发晕配错了环境,然后又重新测试了几次,按捺住心中狂喜。
 我成功了!

 虽然我兴奋得浑身颤抖,张老大并没有表现出有多少吃惊。他一直都在重写一套虚拟磁盘的驱动。那套东西写完之后,那个虚拟磁盘的速度大大提高了。至于是否还有死机的问题,他似乎没有放在心上。不久就直接在客户那里上了他的新版本。我的方法说明和演示都搞了半天,他虽然很有兴趣了解,但是并没有兴趣采用。
 “不错,不错,”看完之后他手一挥,又回到他的电脑堆里去继续干活了。
 我并未失望,我知道虽然他没有使用我的技术,但他对我的看法已经和刚开始不同了。之后,他开始把一些其他的东西交给我做。
 实际上,所有的新员工都开始工作了。除了陈兄弟,还有一伙从各自的天涯海角,聚集到这里来的难兄难弟们。

 几个月之后,劳动合同才发到手中。合同都已经填好,只等着签字了。这时候我们除了签字之外别无选择。因为没有什么刚毕业的学生,愿意带着三个月的工作经验去重找工作的。
 合同一签完字就被收回去,说是要公司盖章。但是有趣的是,虽然签了两份合同,但是却再也没有一份公司发下来。也就是说,公司掌握着我们的合同,而我们却没有。
 唯独陈兄弟没有签合同。他不是不愿意签,而是公司没有发下他的合同来让他签字。不久,他就阴沉着脸对我说,他已经被张老大谈过话,建议他去寻找新的工作机会。
 我不禁觉得后背有一些凉气黯然袭来。虽然我和这位仁兄并没有什么深厚的阶级情谊,但还是感觉白色恐怖加黯然失落。这大概就是传说中的兔死狐悲?陈兄弟见我如此,倒是颇为感动,于是拍拍我的肩膀安慰我道说:“你不用怕。张老大说,他欣赏你这样的工作方式。”
 “哦,他这样说?”
 “恩,我说我有我的工作方式,你有你的工作方式。结果他说,他欣赏你这样的工作方式......”
 我暗想这不是明摆着的么。我这种不要命的每天一早就来,半夜才走,也没有拿过一分钱的加班工资的模范员工,不欣赏我欣赏谁?你这样的准时上班、准时下班,老板是一分多的都捞不到。这有什么适合不适合?
 我那时是急于证实自己的价值。对我来说工作就是一切。寒窗苦读这么多年,不就是为了建功立业?但现在的我观念已经有所不同。工作确实重要,但是工作却仅仅是工作而已。如果工作是人生的全部,那人生还有什么意义可言?以多年的辛苦,换来一身的疾病,和一堆带不进棺材的金银铜铁,真的很值得吗?何况我们努力做着的那些事情,哪一件的初衷,不是为了让人们能更舒适、更悠闲的享受人生呢?建房是为了居住,造车是为了代步、开发软件是为了省心。但是工作狂们却彻底忘记工程的初衷,去重操奴隶时代的旧业并以此荣。 
 第二日陈兄弟的座位就空着了,后来很久都空着。

 许多年过去,那些兄弟都在哪里呢?同事并不像同学一样,会常常的保持联系。往往是一起工作的时候,一起争锋吃醋,又一起吃喝玩乐,一起称兄道弟。而分道扬镳之后,则极少联系。几年之后手机号码更换,从此就人间蒸发,除了偶尔有些传闻之外,就像世上再无这个人了。
 陈兄弟走后,听说去了另一家软件公司,他如鱼得水,薪水比我们高得多。
 第二个倒霉的是老实巴交的雷兄弟,先是说写代码水平不怎么样,被转移到测试部门。后来在测试部门干得也不开心,大约一年半之后就递了辞职信。
 但是他一回家就考了公务员,现在吃着皇粮,相当的优哉游哉。
 还有罗少爷办事麻利,心狠手辣,技术也很牛。和我一样几乎干满三年,然后就扬长而去,直接跑去开公司了。
 人们常常说所谓“适者生存,不适者淘汰”,据说是职场之不二法则,其实并非如此。人类社会并非丛林,并不是非生即死。除了环境可以选择适应它的人类之外,人也可以选择他喜欢的环境。如果在一个地方干得不好,并不就说明自己能力不行。而且往往也并不是如此。如果做得不开心,那就离开就行了。世界之大,无奇不有,又怎么会找不到自己最合适的环境呢?
 树挪死,人挪活。与其说是“适者生存”,还不如说是“适者持续”。所谓的适者,就像老树一样扎下根来,以至于我许多年之后去看望他们,他们还和以前一样的坐在那里。只是办公室显得更小更旧,人也显得不再那么年轻了。
2010-3-5 16:23
0
雪    币: 124
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
82
五 牢笼

    两年多以后,我已经适应了职业程序员的生活。软件开发的事业,与其说是一大帮苦力,在经年累月的堆积代码,不如说是在经年累月的和BUG斗争。无论是老的代码的重新利用,还是为了避免老的代码中的bug而编写的看上去更加干净的新的代码,无论是老产品的移植,还是新产品的诞生,都会带来无数的BUG。而且其中大部分代码他们或者我都已经写过无数遍,但是每一遍都带来新的BUG。
    我觉得这不但不像是一种工程的艺术。相反的,这像是自残或者是某种武士道精神导致的恶果。
    老实说最后我只相信一点:如果一段代码没有被改过,那么至少其中的BUG数量不会增加。所以没事少去改它为好。当然,如果我发现了一个BUG,我会马上改正它,并相信BUG减少了一个。有时候改正BUG也会带来新的BUG,所以修改的时候要尽量谨慎、小心。
    当然,BUG几乎始终存在。这是程序员永远的牢笼。

    Z公司并不从程序的角度采取任何减少BUG,提高软件质量的措施。他们采用人海型的用户测试战术。简而言之就是一大帮人上去点鼠标敲键盘,能把程序搞死的有奖。这方法的确有效,但是并不足够可靠。
    很快我们就发现测试机房的机器清一色都是戴尔原装机,CPU、内存、甚至连键盘都全部一样。但用户的电脑千差万别,从还带着嘎嘎作响的软驱的老古董到最新潮的电脑无所不包。一些在用户处直接当掉的BUG在测试机房都安然无恙。于是我们给测试部门提了意见:
    “测试机房必须要配备从低到高不同配置的电脑、此外还有各种品牌、各种CPU、各种网卡、各种显卡以及各种不同大小和转速的硬盘……”
    测试机房变成大杂烩之后,我们又发现各种不同的软件也会施加不同的影响。比如说一个安装了瑞星或者江民的测试机使用我们的软件没任何问题,但是一个小心得过分的客户既安装了瑞星又安装了江民,再使用我们的金融软件之后系统就崩溃了。
    “这也许是两个杀毒软件互相冲突。您只需要安装一个就行了。”我们的客服人员会这样说。
    客户从不买类似的账:“但是在安装你们的软件之前我的电脑完全是好的!”
    恩,所以即使在疯狂的测试之后,银行的墙上挂着的显示大屏幕上还悬浮着这样一个窗口:“本程序发生了非法操作,即将关闭。确定,取消。”这样的事情也见怪不怪了。
    有趣的是,这就是事实。直到今天,国内许多中小型企业的软件开发的现状都是如此。这些企业的软件,还组成国内信息系统的主要部分。无论是我访问网络银行,还是通过电话订购到一张X票,还是去外地的一场旅行,我都无法确定是不是有部分质量糟糕的软件系统在起关键的作用。
    2010年1月的时候,上海地铁一号线发生一次碰撞的事故。最终查出的结果,是某个信息系统中预存的一个速度数值是错误的。列车因此超速行驶,而造成事故,幸无人伤亡。而这个数字是在9年之前就存入的。
那么,这个数据曾经测试过吗?
2010-3-5 16:26
0
雪    币: 124
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
83
回到五、六年前,那时我还不知道何为单元测试,更不知道分支覆盖、条件覆盖那些测试方法。不过有一点倒是先觉悟了,那就是要减少BUG,最好是写不需要修改,就可以反复被使用的代码。
    我常常疑惑一些函数库的高质量,比如MFC,stl。理论上这些函数库含有大量的代码,而且又不像一个用户软件一样可以雇佣一批劳工疯狂的扑上去通过简单的操作就能详尽测试。为什么它们含有的BUG极少?
    我觉得也许在诞生早期,它们与Z公司产品的BUG一样多。但是之后BUG的数量一定在不断的减少。这些库每应用到一个新的项目中,这个项目就等于对这些库进行了一次测试。而现在应用了MFC和stl的项目已经灿若星河,BUG数量极少也应该在情理之中了。
    不过更重要的是在应用这些库的时候都不需要用户进行修改。如果一个MFC被应用到一个项目之前,必须经过许多的修改才能适应新的项目,从而诞生一个新版本的MFC,毫无疑问,这个新版本一定会增加许多的BUG。那么被应用到十个项目的时候,已经诞生了十个版本的MFC,当然,BUG数量也会爆增了10倍。要命的是其中一个版本的MFC中发现了一个BUG,修改这个BUG也无法同步到其他的版本的MFC中。因为这些版本都已经不一样了,谁还有工夫去检查,到底这是共同的BUG,还是新增加的独有的BUG呢?那么原来的MFC,有多少BUG还是有多少BUG。一步也不会前进。
    MFC和stl它们的特点在于在有限的范围内能被不加修改的重用。我于是决定以后我要写出能重用的代码。如果我的代码能反复的被重用,那么它们的BUG数量一定只会越来越少。这个想法并非凭空而来,而是来源于很多的痛苦的经历。
2010-3-5 16:27
0
雪    币: 124
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
84
在金融软件的统一的华丽的界面之下,其实隐藏着许多不同的组件。这可以用QQ做一个类比。QQ除了用来聊天的QQ窗口之外,还有QQ游戏、QQ音乐,甚至还有一个程序用来做QQ的自动更新。这有个问题,到底是把它们做成一个单独的程序中的不同功能好,还是各自都作为一个单独的程序好?当我的大老板问我的领导张老大这个问题的时候,佛座上的张老大不假思索的回答说:
    “让它们做成单独的程序吧!”
    其实这与其说是为了追求软件运行效率和遵守软件工程管理的原则而做出的正确选择,不如说是为了简单起见。显然,要方便的领导好几个手下,最好的办法就是把独立的程序交给他们开发。这就像一些懒惰的老板,公司做大了,不知道怎么办。就把大公司分成几个小公司,每个儿子去管理一个。
这样的好处是就算一个部分出点问题,也不会影响别的部分。比如金融软件中聊天的部分崩溃了,或许最多非法退出一个进程。而其他金融服务的界面则不受影响。
    于是我们开始分单独的程序各自独立的开发。但是作为一个不小的软件,这些程序之间不得不共享一些东西。比如说一个配置文件。金融服务的主界面程序已经分析读取过它。我一个聊天程序需要用到其中部分的时候,我自然不想自己再去写一些代码来重复分析读取一次。我直接去找著名的余波老兄,他负责编写主程序。
“我也要读取配置文件,你把你读取配置文件的相关的代码给我吧。”
    余波在堆积如山的代码中戴着深度近视眼镜抬起头来。他的压力很大。当然收入也很高。
“这个容易,就一个文件而已。”
    他立刻展开他的代码。这个项目有上百个目录和数千个文件的代码,但是却只有一个工程 。
2010-3-5 16:28
0
雪    币: 124
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
85
我起初的想法很简单,只要把他解析配置文件并读出信息的代码文件拷贝到我的项目里,然后我直接调用其中的函数就可以了。不过我很快发现无法通过编译。原因当然是他的代码文件头上用#include指令包含了其他的头文件,而他却忽视了这件事。
    然后就变成了拉锯战,我向他索取头文件,但是他发送过来的头文件里又包含了其他的头文件。我不得不继续向他索取。某个头文件里出现了这么一句:

    #include “def.h”

    然后再打开def.h这个文件一看,原来里面全部是#include指令,再也没有别的了。显然有人对不断的需要#include头文件感到厌烦了,于是写了一个def.h,里面包含了所有的头文件。从此就省事了。如果他不知道到底缺了哪个头文件,那他就可以写上“#include “def.h””,这是个万金油。
    不过他完全没有想过这给我的计划造成了多么糟糕的打击。这等于我需要把余波兄的巨大工程里所有的头文件都copy过来才能正确编译,而我的目标只不过使用他提供的一到两个简单的读取配置文件的函数而已。与其这样,还不如把他的工程整个拷贝过来算了。
    我不再对不修改这个文件就能使用抱任何希望了。我决定修改余波拷贝给我的文件,提取其中有用的部分,并删除掉那些该死的#include。
    他们之所以要包含那些头文件,是因为读取配置的过程中,配置信息被读入许多数据结构。而这些复杂的数据结构是在很多不同的组件的头文件中定义的。我很快意识到我试图去掉这些#include是不可能的。因为用户配置信息是如此的重要,许多的组件都依赖这些信息,他们都各自定义了自己需要的部分的信息的数据结构。而所谓的“读取配置”这个简单的功能,实际上是需要所有的这些数据结构的。结果也就自然要包含几乎所有组件的头文件了。要避免包含他们,自然唯一的办法就是我去重新定义这些数据结构了。但是如果这样的话,我还不如自己重写一次,简单的读出我自己需要的几个配置信息就完事了。
    以我目前的经验来说,有不少的方法可以写出容易被不加修改的重用的代码。不过有不少同事都认为:“我这段代码是不可能被重用的,因此我怎么写都没关系。”不过实际上,他们往往只能从整体上看到一个段有着特殊应用的程序。却不能发觉,如果把一段看似很特殊的程序拆开,实际上大部分细节都是可以重用的。
    这就像世界上的高楼大厦千奇百怪,很少见完全一样的大楼。这个意义上说,大楼或许是特殊的。但是仔细拆开来,许多大楼都有相似的构造。许多细节的结构都是可以复用的。再细到一桌一椅,一砖一瓦,就莫不可以重用了。
    写代码有意思的地方就在于,如果用完全不考虑可重用性的方式写代码,也可以写出宏伟坚固的软件。但是遗憾的是,这些代码如果不是因为有这个软件的应用价值存在,就完全是一堆毫无意义的垃圾。对一个公司开发下一个软件、或者是未来的发展,都毫无意义。
    如果考虑一下可重用性,只需要注意一些非常简单的规则,就可以写出几十年也不会过时,而且BUG也一定会越来越少的代码。这并不会增加很多的劳动,但是却往往被人忽视。
2010-3-5 16:28
0
雪    币: 124
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
86
一个有趣的例子是命名的前缀。许多项目都有一个名字缩写,比如说一个项目叫做QQ,那么领导往往会要求每个函数或者全局变量都加上Qq作为前缀,比如说:

    QqAllocateMemory ()
    QqFileCopy ()

    但是把所有的函数都加上同一个前缀这本身真的很愚蠢。假定我们公司以后要开发一个新的项目,名字叫做MSN,毫无疑问领导又会要求加上Msn作为前缀。此时,有人注意到文件拷贝的函数在QQ项目里已经实现过了,我们没有必要再写一次。好吧,那么我们把QqFileCopy拿过来。这时就糟了,我们得把所有的Qq前缀改为Msn?这个任务实在是浪费体力,而且可能造成BUG。
    所以我总结的规则是,不要用项目的缩写来作命名的前缀。这样的写法,一开始就潜意识的认为,这份代码只是为这个项目而写的。这真的是鼠目寸光。
    不如以这个函数的功能类别缩写作为前缀。比如说字符串操作的函数,都以Str或者Txt开头。而文件操作类的函数都以File开头。如果把同类功能的函数都放在同一个模块里,则可以以模块名的缩写作为前缀了。这些模块是很适合重用的。
    不过更糟的是以自己名字的缩写作为前缀。我们公司有一个名字缩写为YLS的家伙和老板吵架之后跳去了我们的竞争对手公司。不久市面上就出现了和我们的软件极其类似的对手的软件产品。余波笑着和大老板说:
“这个软件肯定是他写的。”大老板问为何?余波把那些DLL导出的函数展示给老板看:原来那些函数都是以Yls开头的!
    我可不会这样干。这也许会让我臭名昭著。

    另一个有趣的例子是Windows程序员们常常出现有的,一种受MS极其喜欢的习惯。就是把代码写成这样:

    BOOL MyReadConfigFile(CString &file_name)
    {
        BOOL ret;
        HANDLE handle;
        handle = CreateFile(file_name.GetBuffer() …);
        …
    }

    很多人不会同意我的观点。但是我依然觉得,这样的代码对于可重用性来说,完全是垃圾。基本上毫无重用性。只要任意换一个平台,无论是iphone、andvoid、还是linux,都不认识这些古怪的CString、HANDLE,连一个最简单的打开文件的操作CreateFile都不认识。假定要移植代码,该怎么办呢?
    不修改这种代码就实现移植简直就是不可能的。如果要不加修改,但是我们又失去了微软提供的支持,那么就只能自己来定义BOOL,HANDLE,还有CreateFile,这个API里带有大量的我根本不关心的设置参数。布尔类型和句柄也就罢了。CString如何实现?自己重写一套?或者是从微软的库里把代码拷贝出来?这当然可以。不过拷贝的时候,我又不得不发现需要#include一大堆微软提供的头文件。到了最后,我只好几乎把微软提供的整个SDK都拉过来。当然,把它们都迁移到新的平台又是另一个艰巨的任务。而且问题是,这件事和我本来想要做的事毫无关系。
    其实读取配置文件是很简单的一件事。我只需要打开一个文件、读取、关闭,我根本就不需要使用BOOL、HANDLE还有CreateFile这些只有微软的库才提供的符号。
    有些人可能会觉得fopen之类的C标准函数要好一些。有时的确如此。不过我更倾向于完全摆脱外在的依赖性:

    extern int fileOpenSimply(const char *file_name,
        int file_name_len);
    int myReadConfigFile(const char *file_name, int len)
    {
        int ret;
        int file_handle =
            fileOpenSimply(file_name,len);
        …
    }

    我挺喜欢上面风格的代码。虽然有人觉得用int代替BOOL类型不好。如果是这样的话,可以自己去用int定义一个布尔类型,但绝不要直接使用微软的定义。这样的代码的好处是任何地方都能编译成功(只要有C的编译器)。而且在不同的平台下应用,也完全不需要修改这些代码。如果它们没有BUG,那么BUG永远不会增加。如果有BUG,被发现并修改掉一个就会减少一个。在不同的平台下应用,只要实现一个不同平台下的外部函数fileOpenSimply就可以了。这些代码几乎是永恒有价值的,而且减少了大量的#include导致的连锁反应。
2010-3-5 16:29
0
雪    币: 124
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
87
这些规则很简单不是吗?唯一的要诀就是:尽量减少外部依赖。如果非要依赖才能实现,就把它们留在一个很小的范围内。比如说,一个For Windows的模块。至少这个模块很容易替换成For Linux或者For别的。

    在一个工程项目组内,有一些看似很正确的协作规则,却往往限制了每个模块的可重用性。这也是很奇特的现象。比如我开始我在这个巨大金融软件中的小工作的时候,领导提醒我说:
    “你一定要使用余波他们提供的MemAlloc这个函数来进行内存的分配。”
    “你一定要使用张敏他们提供的LogOut这个函数来输出日志。”
    “你一定要使用Oracle的Pro*c来访问数据库。”
    他说的这些固然没有错。把内存分配、输出日志这些模块独立出来,的确是重用这些代码、而且减少工作量的好办法。统一使用数据库的接口,也方便代码阅读和测试,不至于因为访问数据库的方式五花八门而增加额外的测试难度。不过却另外有一点,如果我编写的模块中坚持使用MemAlloc来分配内存的话,毫无疑问我就要包含他们提供的头文件了。这增加了额外的依赖性。换句话说,我写的这个模块,离开他们提供的内存库、还有他们提供的日志模块、甚至Orcale,我的模块就再也没办法使用了。
而且他们这种划分模块,然后直接调用的方法,也会带来所谓循环依赖的问题。比如说我们可以这样提问:
    “内存分配模块中如果遇到有内存分配失败、或者其他的异常情况,是不是应该输出日志?”
    “当然可以。”
    “日志模块中有时是不是也要分配内存?”
    “是的。”
    这样一来,日志模块必须要调用内存分配模块中的函数才能分配内存,而内存模块又需要依赖日志模块才能输出日志。这就变成所谓的循环依赖了。
    其实如果两个被划分成所谓的“独立的”模块如果互相依赖的话,就等于没有划分了。因为他们中任何一个脱离了对方都不可以独立使用。那还叫什么划分成“独立的”模块呢?
    其实光靠功能上的划分是不足以让模块独立的。我于是一开始就在自己的模块中拒绝包含日志模块和内存分配模块的头文件。并且代码中也只调用自己定义的接口去分配内存,而拒绝使用领导规定的接口。
    当然这并不等于我敢于违逆领导的管理。而且我肯定也不会傻到自己去实现一套内存分配。如果是这样的话,等于我又重蹈他们的覆辙了。
    和前面的例子相似,只不过我把文件打开换成了内存分配:

    extern void* myMemAlloc(unsigned int size);
    int myReadConfigFile()
    {
        void *my_block=
        myMemAlloc(512);
        …
    }

    任何时候我需要内存,我都调用一个子虚乌有的函数叫做myMemAlloc去分配。这个函数被我声明了一下。这个声明很简单,不需要任何头文件、其他数据结构的支持。
    当然,连接了我的模块的主程序会无法通过连接,因为myMemAlloc这个函数还没有实现。这时我就按领导的意思,用他规定的接口来实现一下这个函数。当然,这个不算是我模块之内的。我的模块依然是很纯洁的,不含任何对其他模块的依赖关系。
    这个模块甚至可以单独测试,只要把定义的这些extern接口用任何方法实现一下。
    领导没有反对我的写法。不过我觉得那是因为他没有仔细检查我的代码的缘故,他只是简单的以为我遵守了规则。在Z公司,还完全没有代码Review 的制度。
2010-3-5 16:30
0
雪    币: 124
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
88
这两年我干得不错。两年内我一直在不断的加薪(那是因为我刚进去的时候薪水实在太低了!)。还有什么比加薪更好的,获得肯定的方式吗?
    当我的薪水提高到某个层次的时候,我被兼职主管财务的那个副总叫进了办公室。她向我提出,因为我的薪水已经达到了某一个水准,我必须要做出一个选择。
    我很骄傲。难道我现在已经是高收入人士了嘛?虽然我知道显然并非如此,但是潜意识里仍然很满足。
    “你可以和我们签署一份协议。如果你签署了这份协议,我们每月会从你的工资中扣除一部分作为公司内部住房积金,这笔钱存在我们公司。你随时可以退出这个协议来支取这部分钱。但如果你在5年后才支取这笔钱,公司将支付你一倍。你必须现在做出选择。”
    我的大脑快速运转了一分钟,之后我照例做出了错误的选择。我知道我的大脑总是被别人控制做出对自己不利的选择,这几乎成了习惯。
    固然我不希望我的工资的很大一部分被克扣。我可以说:我不签。不过这是一个新的牢笼:如果我不签,这也等于明白的和老板表露:我并不想在这里工作五年。我打算五年内跳槽了。
    其实这不算什么。但是那时我总是害怕。如果老板知道我五年内就要走人,会不会不再信任,避免把关键的任务交给我做呢?如果是这样的话,发展就要受到巨大的限制了。
    如果换了现在,我就会大声的说:“非常的抱歉。虽然我现在对公司很满意,但是我是否在这里工作五年,取决于这五年我在公司是否得到自己满意的发展和报酬,以及我工作是否愉快。如果是肯定的,没有这个该死的住房补助计划我也照样会留在公司。如果这个是否定的,对不起,我早拍屁股走人了。所以我对这个每月要克扣我这么大笔钱的牢笼计划完全不感兴趣。”
    不过那时我说:“好吧,我签。”
    所谓的“随时可以支取”其实是个幌子。首先我如果去取这笔钱就等于和老板说:“我打算走人了,所以先取钱。”更重要的是协议是协议,实际是实际。如果老板说:“对不起,最近公司的现金情况比较紧张,暂时不能把这笔钱提取给你。等三个月之后,我们一定支付。”那时我应该如何办?或许三个月之后,我已经找好的新工作也黄了。如何办?向法院提起诉讼么?就算诉讼取回钱,也远远超过三个月了。
    恩,这就是事实。在之后的一年里,我每月拿到的薪水只不过我应该得到的薪水的四分之三。等一年之后我离开公司的时候,副总很客气的和我说:“这笔积金作为你的跳槽违约金计算了。”
    不过据我所知,我是这个公司历史上唯一因为跳槽而支付违约金的人。而且我手里还没有劳动合同。
    好吧,最糟糕的老板和雇员的关系莫过于此。雇员失去了他应得的薪水,而老板得到了塞不满他牙缝的一笔小钱。一方因此而愤愤不平,另一方为此而臭名昭著。
2010-3-5 16:30
0
雪    币: 124
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
89
有一天我收到一封邮件,问我愿不愿意加入一个有名的日本公司N公司在上海的一个分支。他们在那里有一个开发中心,为日本的母公司开发软件。邮件里他们开了一个让我耳目一新的薪酬。
    我猜他们可能是从某些技术论坛上得到我的邮箱地址。我常常逛论坛并讨论技术问题。可见在技术论坛上留下自己的Email是很有好处的。
    虽然我对国际化的大公司很感兴趣,但是我不喜欢日本人。这时候抵制日货的运动风起云涌,甚至有人游行砸寿司和拉面店。我直接就回复拒绝了。我说我对日企没有兴趣。
    第二天他们再次回复了邮件。发信的是中国人,他竟然说他其实也是“反日份子”。而且薪酬的数量居然加了一千。
    我再次回信回绝了。
    很快又来了第三封邮件。薪水又提了一千!好吧,我在这里工作了两年,虽然每半年都有加薪,但是加薪的总数也没有超过这两封邮件的来回!如果我现在接受的话,等于我的薪水在一周内就整体翻了一番,而且也不用被扣掉所谓“内部的住房积金”了。
    而且我也会离开南京这种“废都”,去看看所谓的“现代化的国际大都市”了。
    要再次拒绝真的是很困难的一件事。
    就算接受又怎么样呢?成为汉奸?至少如果没有为日本企业工作过,就学不到日本企业里先进的东西。抵制是没意义的。抵制或者是闭关锁国的一种表现。而闭关锁国是不对的,这从清朝的衰落上就可以看出来。至少我不能老在这个各种阴险合同和协议组成的小笼子里混下去,简直是蹉跎岁月。
    其实这些大公司的就业机会,中国人拒绝的话,只不过把它们拱手让给印度人、越南人和菲律宾人而已。活总是能找到人干的。与其让别国的人做,还不如让自己人做,不但学到了技术,还领了薪水。
    再者,就算跳槽到了一家日本公司,也没有说就要一辈子为日本人效力。等过几年再跳槽到欧美公司,或者干脆豁了老命的去华为炸碉堡、堵抢眼,也不为迟吧?
    既然如此,那就要认真的回复一下邮件了。
2010-3-5 16:31
0
雪    币: 206
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
90
写得好。。代码重用的那部分让我这个菜鸟程序员深有共鸣。。
2010-3-6 10:24
0
雪    币: 558
活跃值: (43)
能力值: ( LV12,RANK:220 )
在线值:
发帖
回帖
粉丝
91
表示支持,UP
2010-3-23 09:24
0
雪    币: 2087
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
92
学习了,很有参考价值。
2010-3-23 11:42
0
雪    币: 124
活跃值: (43)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
93
1990的  同样期待 。。。
2010-3-23 13:07
0
雪    币: 399
活跃值: (38)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
94
真钓胃口啊,继续期待续集
2010-4-4 11:42
0
雪    币: 209
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
95
mark,强烈支持,期待完整版pdf...
2010-4-4 22:16
0
雪    币: 351
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
96
结束了吗?好久没有更新了啊。。。
2010-7-14 15:31
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
97
mark~~~~~
2010-7-31 15:45
0
雪    币: 401
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
98
等待下文,全部结束后再看。。。
2010-7-31 16:18
0
雪    币: 399
活跃值: (38)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
99
最新消息,楚狂人将不再更新此文,遗憾
2010-7-31 19:13
0
雪    币: 401
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
100
不更新了?为啥啊……唉,那我就可以开始看了
2010-7-31 19:32
0
游客
登录 | 注册 方可回帖
返回
//