编程如写作

一直以来,我常常为这个问题而很恼火,我们软件的每个功能点都不复杂,算法更不深奥,但是错误总是层出不穷,为什么?
有一天我突然想到一个答案,那就是程序的清晰性。我觉得,所有的程序设计语言,例如C、C++、Vb、Java、Delphi、C#,它们都是一门语言。既然是语言,就应该通俗易懂,越简单明了,程序员就越不会出错。这跟写作的道理何尝不是一样的。
但是这个道理,我想大多数讲程序设计语言的书籍都没有讲,至少我没有看过,也没有听说有这种书,但程序的清晰性却确实影响着我们程序的质量,只不过它一般由程序员根据自己的感觉来保障。可惜的是这跟程序员的经验、细心程度大有关系。
作为一个IT界的菜鸟,我真正写过的代码很少,到现在为止恐怕也不过七八千行,所幸的是,我还算一个爱思考的人,所以还是发现了一些东西。现在我发现的会影响程序清晰性的东西如下:
1 函数的上下文无关性。我们的软件中常常碰到这样的情况,A函数调用了B函数,B函数调用C函数,单看每一个函数,包括它们的注释,我完全不知道这些函数是干嘛的,在我花了很长时间,追根溯源,反复对照后,我才恍然大悟,起初我为此而泄气,觉得自己的智商实在很低,这么简单的逻辑怎么看不懂呢,后来我才意识到,不是我太笨,而是函数过多的依赖上下文,太晦涩了。就像单听老者考也,考者老也,谁也不知道老和考是什么。
2 类型、变量、函数等命名的直观。变量的命名有一个度,短了不知道干啥用的,长了得看不天,很费劲。我见过最搞笑的命名是这么一件事,大四下半学期,在做毕设的时候,有一次,一个同学让我帮调一下程序,然后我看见了几个函数junjun1()、junjun2(),这个同学我们都叫他军军,所以他把自己的绰号欣然引进了自己的程序,但天知道1个月后,也许只要3天,他会知道这个函数是用来干嘛的,其实这种事情在刚学编程的人中比比皆是。
3 学过C语言的人,对这个语句肯定特别熟悉,
string A;
for (int i=0;i<A.length();i++)
 cout << A[i] << endl;
这个循环的起始条件是数组的0号元素,而结束是数组长度-1号元素,天啦,为什么C语言要用0作为数组的起点,这样我们可以这样写
for(int i=1;i<=A.length();i++)
多明了,如果这时候你还出错,你只能找块豆腐,撞两百下。
现在的例子很简单,你觉得不容易出错,但是以我的经验,在较复杂的处理中,写错循环终止条件导致数组越界访问的事经常发生。

后来室友与我讨论认为,可能C语言的创造者,起先是出于性能考虑,以0作为数组的起点时,比如对char str[10];A+n只要把首地址加上n个字节就可以寻址,更快速,如果从1起点,还要减一,不过到了今天,廉价的硬件早已让这种做法失去了价值。
4
//common.h
typedef std::vector<std::string> strvtr;
//common.cpp
strvtr strTmp;
...
//处理strTmp;
如果在VC中,把鼠标放到这个变量上,VC会显示一个悬浮窗strvtr strTmp;提示这个变量的类型,这时候你会知道strvtr是个什么类型吗,也许你能猜出来,也许猜不出来,猜不出来,你就得追溯到这个类型的定义处,也许你会碰到
typedef typeA typeB;
typedef typeB typeC;
...
typedef typeM typeN;
而且这些typedef还不是放在一个文件中,看到这里,你只想抽那个这么写的程序员,这种情况,在以前我看北电传输网管corba接口的idl文件时,就碰到过,而且比比皆是。我常常要做大量的查询工作,费很大的劲才能找到某个类型的真实定义。
由此可见,为了一时的方便,却给程序清晰性带来的巨大损害。
其实像这样定义
std::vector<std::string> strvtr;
定义的语句也不是很长,关键是很清晰,一看到变量的这种提示,你就会知道它的类型。
补充:采用typedef并非一无是处,它用来使一个名称较长的类型更简单,易于使用,就好像你站在向阳小区1号楼1单元门口,对别人说,我住602,别人基本会认为你就住在向阳小区1号楼1单元602,当然如果你指指单元的门说,我住602,意思就会更清晰,但是如果你站在北三环上,这么说,恐怕只有那些本来就知道你住哪儿的人(比如你和你的朋友)知道你住哪儿,所以typedef不是不可以用,但要保证其使用的上下文环境足够清晰。
5 循环中我们常常使用单个的字母作为计数器,例如i、j、k
在循环套用的时候,如下
char s2Ar[4][4] = {"ok","no","yes","hi"};
for (int i=0;i<4;i++){
 处理s2Ar[i]
 for(int j=0;j<4;j++){
  处理s2Ar[i][j]
 }
}
上面就很容易把i和j混淆,特别是循环较深的时候,造成甚至越界访问这样严重的错误,如果使用iRow和iCol计数,即使写错了一两个字母,编译器也会认为未定义类型而报错

今天讨论到一个问题,我们软件的报错连开发人员也看不懂,以至常常打电话让本地维护人员帮忙调程序,我说,是因为编程时,我们依赖了那种上下文的环境,对错误做出一个粗略的解释,那时我们明白它的所指,但是时间久远后,离开了上下文,再看这个报错,就不知所曰了。我一位室友说,程序员会出错,就是因为总耍些小聪明,如果程序里只有if/else,你怎么能出错呢,程序员更应该由笨人来做。我仔细想想,发现这与上面的结论不谋而合,程序应该清晰,越傻瓜越好。我们总是自以为自己能应付这些复杂的逻辑,复杂的结构,但是我们失败了,以至自己焦头烂额。

上面是我发现的一些影响程序清晰性的东西,以后我还会继续努力去发现更多的和对它们的解决办法。也许有些人觉得这不值一提,或许会说差不多就行了,这样还可以锻炼我的逻辑思维,这让我想起一件事,我们大学有一栋楼的2层和4层都供应开水,但我一开始一直不知道,所以总跑到4楼去打,即使我在1楼的时候,后来我知道了,你说我还应该去4楼吗,不要告诉我那样可以锻炼身体,因为它的活动量太小,实在不值得我花费时间,就像因为程序的不清晰锻炼了我的逻辑思维一样,我想我该把寄托于一些更深层东西的思考上。
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值