计算机基础知识对程序员来说有多重要?

大约08年,我在一家公司写了大约800行代码。这800行代码可以一劳永逸的替代他们已有的二三十万行满是缺陷的垃圾。

事情是这样的:他们搞了个很复杂的系统,我不想讨论这个系统设计的有多烂了;总之,他们需要搞几百个不同的报文,在网络上传来传去。每个报文长短不一,有的可能只需三五个字段,有的得有三五十个甚至更多。由于设计水平问题,绝大多数报文字段数量都很大。

C++数据结构是不能直接在网络上传输的;尤其这个系统有C++写的部分,也有java写的部分。因此必须先转换成网络报文,然后才能提交给网卡传输;等对方收到报文后,必须解析这个报文、识别它的数据结构、然后在把它转换回来。

报文类型太多,他们的程序员能力又……嗯嗯,稀烂;所以他们决定,所有这些数据结构都要转换成XML传输,对端收到后,再解析XML、还原数据。

一来一回,每个报文往往需要少则两三百行、多则三四千行代码,这才能完成数据收发工作。几百个数据结构综合起来,代码量轻松破几十万。


因为程序员能力问题,他们这个系统的水平啊……真是一言难尽。

只说一点吧:他们居然把debug版的assert重新定义为空,因为……他们害怕崩溃。

可是,掩耳盗铃就真能治崩溃吗?

有一次,一位同事愣是跨越不知多少层逻辑,把自己的XML灌进了我的执行栈。

程序崩了,找我;打开core一看,我的程序栈被灌了一个一塌糊涂,内容是一个八竿子打不着的XML。我就问这个表格是谁负责的,这才找到他。

然后,这位找到一个一千多行的函数,前面几十行拷到末尾,末尾几十行拷到前面,中间再呼啦删掉几十行……折腾大半个小时,告诉我“好了”。

一运行,程序崩了。崩在他的代码里面。

当时一位和我关系很好的、华为出来的同事实在看不下去了:“你写程序都碰运气的吗?来,看这字符串内容,看它和哪个变量有关……排错是需要逻辑的!哎呀你怎么不用循环?算了算了先排错……”

这位还一脸不忿:“看看!编译错误!我的起码能编译!”

没理他。五分钟后,问题解决。

和另一个组同时接的一个任务,我们一周完成,然后找经理要第二个、第三个任务,等做到第四个甚至第五个任务了,这个组终于姗姗来迟,完成了自己的任务——然后装的很努力的样子,星期天到公司加班联调。

然后,他们十万火急的喊我去公司,说我程序有bug;问是什么bug,不答。过去一看,需求没写清楚,只说某个字段是一个字符串;我按C惯例,后面留了个\0;他们认为这里不应该有\0,五六个人就干等我过去……

删掉\0,数据传输正确,对方的模块立刻崩溃。检查再三,数据没问题,他们自己写出bug了——这个谁负责的?他来了没?出差了?那调不了,大家先玩吧。

就这么个工作态度。

可想而知,这个系统会是什么水平。


总之,不吐槽他们了。先解决问题。

这个问题我是这么分析的:

1、基本数据类型有限

事实上,每种不同的数据类型,打包/解包流程全都一样。因此不应该每个报文从头敲代码,重复劳动太多了。

如果把它们写成诸如int2xml/xml2int、str2xml/xml2str,那么代码量就会大幅降低。或许不到十万行就能解决问题——等于代码量下降起码三倍。

事实上,C++支持泛型。完全可以统一搞成个var2xml/xml2var,类型推导系统会自动推导出正确类型、生成正确的代码——至多针对特殊数据类型做一点特化。

这样基础数据的打包/解包操作会更简单,代码量可以进一步降低——因为无需判断数据类型了,对每个字段调用var2xml(s.item, str_buf)就行了,泛型系统会自动选择正确实现。

用这种方法写,或许三五万行代码就能解决问题。

2、所谓数据结构,其实不过是基本数据类型的组合

因此,如果可以让程序“知道”某个数据结构不同偏移位置的数据类型,那么完全可以统一处理所有报文。

比如说吧:

struct login {
   char username[20];
   int ID;
}

如果程序能从login这个类型,知道它的开头是20个字节的char数组、然后第21个字节开始是一个int的话,自动从login转换成xml报文或者从xml报文重建login,就是很简单的一件事了。

但是,C++本身是支持不了这个的。怎么办呢?

学MFC,玩宏:

DECLARE_TABLE(login)
DECLARE_TABLE_ITEM(login, username, char, 20)
DECLARE_TABLE_ITEM(login, ID, int, 4)
END_TABLE_DECLARE(login)

用这个方式接管struct声明过程,我们就可以在宏里面玩猫腻了。

我的做法是,DECLARE_TABLE里面其实没有生成login这个struct,而是声明了一个login_details的数组;然后DECLARE_TABLE_ITEM其实是在初始化这个数组,把不同偏移位置的数据类型记录下来。

直到END_TABLE_DECLARE这行,login这个struct才真正建立。

然后就简单了。写一对模板函数struct2xml/xml2struct,利用模板推导,自动查找typename##_details数组(这个##是gcc的扩展,用来拼接字符串),关于typename的细节就到手了。然后逐项处理这个数组,自然就完成了打包/解包工作。

这个东西类似于后来名声显赫的protobuf;只是我那时技术视野还不够宽广,仿效对象是不够优雅的MFC。因此缺陷颇多,看起来诘屈聱牙,还用了gcc的关于宏的一些非标扩展,较难维护。等后来见了ORMprotobuf的实现思路之后,我才知道自己的笨拙。

它一共花了我两三周时间,写了800多行代码。照例,一次编译通过,测了一周,完全可支持各种报表数据。

现在,只要把原始的结构体声明替换成这么一组DECLARE宏(可以写一个小程序转换),然后需要xml时调用struct2xml,需要从xml还原时先识别报文类型再调用xml2struct——还是那句话,无需区分,无需记忆,类型推导系统会自动帮你选择。

现在,让我们对比一下。

过去,为了打包/解包这些报表,按共200个报表(其实还不止这么多……人家的设计就是信手画个蜘蛛网,一个心跳处理都能画十几个方块、几十条线路,包含五六种不同报文)、每个报表有二十个字段、每个报表打包解包一两千行算(因为每个字段都要复制,要在xml中记录和校验长度,要写日志方便追查执行流程;而且网上收到的每个xml报文都需要先解析xml、确认每个字段名称/;加上他们很多人不用循环,写的又臭又长,每个字段用五十到一百行代码完成打包/解包并不算多。注意注释也算代码行数),共需20到40万行代码。

四年共1460天,208周;那么按三十行代码算,一个人必须每周开发1400多行代码,才能写完这些代码。

然后,这每周1400行代码,又得多少测试,才能堪堪够用呢?

而我这800行代码,写完再不用动。想找出所有bug,是不是容易太多?

然后,随便你想个什么报文,用DECLARE宏一声明,struct2xml然后发送,或者先接收、识别报文类型然后xml2struct解包……完全搞定。

你看,这只是编程语言基础知识的一点小小应用。会了,你就可以955、然后用三周时间的800来行代码,碾压别人007连轴转、每周1400行代码忙碌四年的成果。

而且,800行代码可以一次写出0bug;每周1400行代码写四年,又岂是另外一个四年能抓尽bug的。

当然,这只是个简单案例。因此一个基础知识掌握较好的“仅仅”以5、60倍的效率碾压了那些基础知识没有很好掌握的。稍微复杂一些的情况下,好程序员效率百倍于差程序员的情形,都是业内人士司空见惯的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值