本文列出的10个错误,并不局限于C#,Delphi,JavaScript等——几乎涵盖了所有的编程语言。是不是大吹大擂,欢迎各位品鉴……
当人们使用编译器创建自己的app时,在把自己的想法诉诸于机器代码的过程中,常常会将那些可以使得编程更为简单却又冗长的语法遗忘于脑后。
无论你使用的是单字母的标识符还是更易于人脑理解的标识符,对于编译器而言,毫无区别。编译器不在乎你写的是否是优化表达式,也不在乎你是否用括号封装了子表达式。编译器要做的就是将这些人脑可读的代码,解析为抽象的语法树,并将这些树转换成机器代码,或某种中间语言。
那么,为什么不使用更可读或者语义更明显的标识符呢——而不要仅仅是I,J或x。老实说,现在我们用来等待编译器完成转换标识符的时间几乎是微不足道。但是,这么做却可以大大减少你和其他程序员用于阅读理解这些源代码所用的时间。
还有一个类似的观点是:或许你可能已经记住了相关的运算符优先级,于是省略了表达式中一些不必要的括号,但是却没有考虑到后面的程序员有可能会误读你的代码,并就它是如何工作的作出一些无效的假设。
我的想法是,假设大家都知道,乘法(或除法)优先于加法和减法。其他任何我放到表达式中的内容我都会用上括号,以确保能真正表达我的意思,其他人也能真正理解我的想法。
有研究表明,有的代码维护所需要的时间甚至超出其编写时间的五倍以上。所以将代码写得易于阅读和理解是非常有意义的。
有一个经验法则就是,我们写的程序不应该过于庞大。而且我们也可以发现,现在方法趋向于越来越小巧——有时候仅仅只是几行代码。
从本质上说,要想快速把握程序的目的和意义,只需要一定的代码就够了。长方法不但令人难以接受,而且往往最终趋向于支离破碎。
其原因也非常简单:长方法既难以理解,又难以维护,甚至还难以正常测试。
有一个相当不错的测量方法可以衡量你的代码的复杂程度,以及出现bug的概率—— 循环复杂度。
该方法由Thomas J. McCabe Sr于1976年开发。循环复杂度使用方便简单,能让你在匆忙之中尽可能地保证代码运行正常。只需要数一数代码中‘if’语句和循环的数量,再加1,就是该方法的CC值。
当然这只是对代码执行路径数量的粗略计数。不过,如果你的某个方法其循环复杂度值大于10,我建议你重写。
这一点非常简单。当我们在编写代码的时候,有时我们会自作聪明地对某些代码过于注重细节过于精益求精,虽然看上去这些“明智”的代码比原先写的那些提高了速度,但是你忽略了一个事实,这些“明智”的代码往往是难以阅读难以理解的——而且真正节省的时间往往只有几毫秒。这就是所谓的过早的优化。
著名的计算机科学家Donald Knuth曾经说过,“过早的优化是一切罪恶的根源”。
换言之就是:我们的代码需要清晰、干净,然后再重点找出真正的瓶颈并对其进行优化。千万不要试图过早的优化。
话说回来,有的编程语言是完全没有局部变量这个概念的,所以不得不使用全局变量。关于全局变量,虽然我们可以在子函数中使用它,但是却没办法声明这一变量只能在该函数中使用。尽管如此,全局变量依然非常受欢迎,因为我们只需声明一次,即可到处使用,太省时省力了有木有。
但是它的优点也是它的缺陷,这也是关于全局变量最糟糕的事情——我们没有办法控制它的改变,也没办法控制何时去访问变量。假设某个全局变量在调用到程序之前赋予了一个特定的值,但是很可能调用完了之后值就变了,而你却毫无察觉。
你的目标是写一个应用程序,你斗志昂扬,愈战愈勇。但是突然间,你发现了性能问题和内存不足的问题。
进一步的调查表明,尽管你的设计对于现在这样小型的用户数量、记录、条目运行良好,但是却不适合大规模的情况——Twitter就是例子。又或者它现在在你的8GB RAM和SSD的3GHz PC上运行顺畅,但一旦到普通的PC上,它会比乌龟爬还要慢吞吞。
所以,部分设计进程还是需要评估,需要一系列的封底计算。有多少用户需要同时处理多少个用户?需要处理多少记录?目标响应时间又是多少?等等。
尽量对这些类型的问题进行评估,这样就可以对应用程序中的一些技术问题做一些更进一步的决策,如不同的算法和缓存。不要什么乱七八糟的都纳入到开发中去——你还需要好好评估目标和目的。
这个错误基本上每一个程序员都犯过,通常在写循环的时候,由于循环变量的步长增加过多或过少,导致循环遍历元素的次数发生错误,产生数组溢出的异常。
这个错误会导致遍历数组元素时访问不存在的元素,或者遗漏应该遍历的元素。产生这个错误的原因就是你忘记了数组下标是从0开始还是从1开始了。