剑客
关注科技互联网

程序总出故障,这10个容易犯的编程错误你避免了吗?

【51CTO.com快译】为什么程序出故障?虽然自世界上第一位女程序员艾达·洛夫莱斯(Ada Lovelace)在上世纪第一次看到通用计算的潜力以来我们已取得了很大进展,但是我们编写的软件还是错误百出。这些年来,尽管我们开发出许多高级方法来确保代码的成功,但是程序还是不断的出故障。

原因何在?

虽然这个问题的答案多种多样,但我们还是决定提供一个务实的答案。程序员难免犯错。他们有时马虎了事。他们并不总是使用最佳工具或最佳实践。

程序总出故障,这10个容易犯的编程错误你避免了吗?

我在加州大学伯克利分校教面向对象编程这门课,我在学校教优秀编程实践所花的时间与帮助学生理解代码本身所花的时间相比只多不少。我在课堂上看到许多常犯的错误,本文就介绍其中几个常见错误。

我还联系上了西北理工大学工程学院的詹姆斯·A·康纳(James A. Connor)教授,请他介绍其学生常犯的一些错误。

我先来说几个。

第一个错误:糟糕的注释方法。

注释是程序里面计算机并不执行的那部分文本。它们被程序员写成附注的形式,用来解释代码里面发生的情况。

我的好多学生避免给代码添加注释,也想不明白为何要占用实际编码的时间去编写一些注释。我最实用的例子来自我自己的生活。

早在世纪之交前,我编写了版本1.0的ZENPRESS,这是最古老的内容管理系统之一。我预计它会带来好几年的文章。14年过后,它仍在管理许多文章,准备好了75000篇文章和26亿页的内容。

最后,它运行所依赖的那个平台过时了。我不得不回过头去研究代码。2009年,我把代码从原始平台移植到现代平台。我最近不得不再次改动,因为PHP一个关键的语言特性在版本升级后完全消失了。

程序总出故障,这10个容易犯的编程错误你避免了吗?

19年过后,我根本记不起所有这些代码是怎么运行的,但是由于我对代码作了详细的注释,所以可以说有了一份路线图。我可以查看代码,查看嵌入在代码里面的注释,然后进行修改。

你在团队工作时,或者你的软件不归你监管时,注释也很重要。你的职业生涯可能发生变化,别人可能需要过来了解你的代码。注释将大有帮助。

第二个错误:糟糕的变量命名。

我会继续探讨这个主题:通过语言让代码一目了然。我会用一个例子来表明这点。假设你驾驶一辆每加仑汽油跑20英里的汽车,开了100英里。请问你用掉了多少汽油?

这是个简单的例子,但是适用于我们的用途。假设你遇到了a = b/c这一行。a指什么?b和c又指什么?它们与你的其余代码有何关系?在编写程序十分钟后,你会记得一干二净。更不用说别人过来修改代码或编写更新版了。

现在看看这个表达式:加仑 = 英里/每加仑行驶英里数。每一个变量的具体用途就一目了然。一个代表加仑,一个代表英里,另一个代表每加仑行驶英里数。很清楚。

想一想为变量赋予清楚的英语(或者其他任何母语)名称与注释之间的关系。假设你从别人那里接过了一段代码,看到a = b/c。这代码用来干嘛?你有何头绪吗?

务必以一种代表其功能的方式来命名变量。那样可以节省大量的时间,减少许多头痛的问题。

第三个错误:没有实验笔记(lab notes)。

我在1997年年中开始编写ZENPRESS,它在1998年1月份上线。遗憾的是,我当时匆忙完成了项目,没有花时间为这第一个版本编写实验笔记。此后我懊悔多次。从1999年6月份开始(当时我开始编写版本2),我就经常做实验笔记了。

程序总出故障,这10个容易犯的编程错误你避免了吗?

实验笔记是代码注释之外的记录。科学家一直在使用实验笔记,作为其研发过程的日志或对话内容。实验笔记用来证明科学发现归谁所有,因为研究过程常常记录在科学家用来记录进度的每天日志中。

实验笔记对程序员来说同样是一种有效的工具。我为ZENPRESS编写的上一份实验笔记是在今年3月份编写的,当时我不得不把ZATZ归档从一家主机托管提供商迁移到另一家。我还经常对自己的其他项目做实验笔记,由于能够回过头去查阅笔记,好多次帮了大忙。

如果你还没有做实验笔记,现在就开始做。记下你做的任何变化,你的理由,你考虑后丢弃的代码,参考的实用资源,以及将来帮助你的任何其他内容。你还能帮助将来的同事或接手人――如果你需要证明归属权,实验笔记还能起到证明的作用。

第四个错误:不用一种人类语言编写。

我的学生要考试过关,不仅仅要编程,他们还要编写讨论区帖子,证明他们熟知某些编程概念。

我们提出这个要求出于两个原因。当然,首先是为了证明熟悉概念。但是更为重要的是需要所有专业人员都有编写能力。

我在这方面遭到学生们的反对。每学期都有学生高喊:“我想成为程序员,而不是编写者。”但是编程、工程、IT和几乎所有专业工作都不可能在真空状态下存在。

你需要通过编写来解释概念、推销想法、获得资金、要求澄清、准备提议,或者甚至为拿到更好的分数据理力争。开源项目的参与者在非常庞大的团队协同工作,他们保持同步的唯一手段就是编写清楚的、易于理解的信息。

结论很简单:如果你想要从事专业工作或从事任何重要的项目,就需要用一种人类语言(比如英语)来编写,而不仅仅是用一种编程语言来编写。

第五个错误:糟糕的代码格式。

毫无疑问,这里的一个主题就是:让代码易于了解。代码维护起来极耗费时间和财力。坦率地说,这也不是很有趣。最好还是能够把宝贵的工作时间用来添加功能,而不是花几周来钻研旧代码,试图搞清楚你(或者交给你代码的那个人)想要完成什么任务。

本人就遇到过这种事,不仅仅是来自我的旧代码,还来自从别人手里接过的代码。我接手被丢弃的WordPress 开源插件作为一个副带项目。据我所知,我接手的插件比其他任何人都要多。每个插件都是由别人开发的,为了保持插件可以正常使用,我不得不钻研陌生人的代码。

幸好,那些开发人员都是高手,深谙编程之道。要不然,我也就无法接手这些项目了。但即便如此,要尽快上手还是困难重重。你能想象要是他们编写的代码结构很糟糕,那会有多难吗?

我所说的结构是指代码的布局方式。我为学生制作了这方面的一段视频。有兴趣的话,大家可以上YouTube观看(https://youtu.be/0u-I016Hxlw)。

想一想你在网上读到的文章。一些文章格式优美,每一个段落之间有一行,一切都保持一致。可是有些文章都用一个大大的blob来排列,没法看清。

每个程序员(或项目)都往往有一种编程风格。你的风格是什么样不是同样重要,只要保持一致就行。你需要让代码格式来帮助引导。

比如说,在我的代码中,我坚持代码段之间的空行不得超过一行。如果我看到一段更大的空白区,我立即就知道这一点:哪里出现了异常,这个空白区里面可能有错误。

你在深入研究代码时,要关注贵企业有没有编程风格。考虑为你的所有程序员定义一种编程风格,坚持采用清楚、易于维护的那一种风格。

第六个错误:糟糕的错误检查。

某位著名的将军曾经说过,遇到敌人时,计划根本不管用。我在此基础上改动一下,遇到用户时,你的代码根本不管用。尽管你认为自己知道用户会如何使用代码,但你其实并不知道,相信我。

用户会搞坏你的代码。

正确的处理方式就是借助测试和错误检查。错误检查是指这种做法:检查代码中每一次操作的结果。确保它符合你的预期,或者确保你的代码可处理意外的结果。

程序总出故障,这10个容易犯的编程错误你避免了吗?

比如说,我的学生经常有一项任务:阅读文件。几乎所有的学生编写代码时会调用文件读取例程。他们检查用户是否取消对话框,但是很少查看文件是否实际上被读入,或者是否存在某种类型的系统错误。要是他们试图编写文件,那就更糟糕了。他们几乎从不真正查看文件是不是实际上保存起来。真是要命。

不难发现这会有多糟糕。为了对付这种情形,你总是要考虑能不能绝对预测行为,然后认识到你不能绝对预测行为。你需要测试。测试并不是仅仅指你自己运行代码。测试意味着让实际用户(即行为可能无法预测的那些人)运行你的代码。

你会发现这会提供大量的信息。

第七个错误:使用打印输出语句,而不是真正的调试器。

这些年来我发现,使用不同语言的程序员往往有不同的文化。总的来说,那是由于他们构建不同种类的解决方案、使用不同的工具。

这方面的一个例子就是我的C#编程学生和与我一起开发一些项目的开源PHP开发人员之间的区别。几乎没有一个C#程序员会考虑不使用一种符号调试器来调试代码。那是由于, C#本身是使用Visual Studio作为编程环境来编写的,调试器内置在里面。

相比之下,我见到许多PHP开发人员认为只要插入echo语句或var_dump就足以帮助自己调试代码了。这一方面是由于,大多数PHP程序员往往在编辑器里面编程,而不是在开发环境里面编程。两者之间的一大区别就是调试器。

那么,调试器是什么东东?简而言之,这种工具让你可以在代码运行时查看代码内部的情况。它就好比是代码的X光、超声波或MRI。可以指令调试器在某些点停止,检查所有变量的状态。还可以指令调试器在某些条件下停止。你可以更改值,可以查看和分析值(不过分析有时是另一种工具)。

工作效率方面的差异很大。如果你想更快速、极其准确地完成工作,就要确保使用一种真正的符号调试器。

以上就是我所介绍的几个常见错误,下面看看詹姆斯·康纳教授介绍的几个。

第八个错误:使用魔数(magical number)。

许多程序员认为,他们只要编写一次代码,代码就能完美无缺。然而,为了优化企业软件和工业软件的长期生命周期成本,有必要编写能够抵御不断变化的条件的代码。

这方面的一个典例就是魔数这个想法。我所说的魔数是指程序员认为总能经受得住时间考验的数字。

以可能基于客户的采购数量的佣金计算为例。截至截稿时,佣金比例可能是三个百分点,即0.03。

现在,设想一下会如何编写这段代码:佣金= .03 *销售额。在这个上下文中,这个魔数就是0.03。由于程序员认为这会是永远神奇地有效,他将0.03这个数字硬编码到代码中。

这一切很好,但是每年的佣金往往发生变化。如果下一年佣金涨了0.5%,涨到0.035,那么就很难在成千上万行代码中找到它。

切忌使用魔数,而是在一个地方定义变量或常量,让代码使用那些变量。如果你预先定义commission_rate,那么commission = commission_rate * sale之类的代码就不需要改动。

要考虑的另一个方面是,无论你在何处找到魔数,都应该找到想要提供给用户的选项,以便用户可以在偏好设置部分可以设置。

第九个错误:马虎对待的日期和时间。

这里有个难题:一年有几天?365天也许是平常的回答,但今年当然有366天。一天会有365.25天吗?这不可能。

但我的一些学生认为,既然闰年每四年就出现一次,所以每年平均下来因此是365.25天。在进行日期计算时,他们使用这个平均值;因而,结果根本不正确。

常常更好的办法是使用系统库来计算日期,因为你计算的日期可能不是西方日历日期。

不妨看一下时间方面的类似问题。每几年,由于地球转速减慢,有一天会多出一秒来,通常是在6月30日或12月31日那天。这叫作闰秒,因而,时钟可能从11:59:59走到11:59:60再走到12:00:00。

这是第二个时间挑战。在使用夏令时的地方,交易的进行有可能乱套。比如说,先置入交易A,但是随后时间被向后重置1小时,那么就置入交易B。然而,如果你在时间序列方面很马虎,它就会记录为交易B先发生。这种类型的时间错误会导致产生不必要的罚款,还会导致其他各种各样的混乱。

再次,有许多好的语言和系统库可以处理这两种时间问题。常常更好的办法是使用现有的库,而不是编写自己的时间计算代码。

第十个错误:没有选择合适的数据结构。

数据结构是表示程序中数据的一种机制。许多人听说过链表、树和数组之类的术语。这每一个术语都是数据的逻辑表示,对应于你试图表示的数据的一些架构结构。

我看到程序员(无论编程高手还是菜鸟)最常犯的错误之一就是,很少注意数据结构方面的选择。由于你的几乎所有代码都建立在选择的那种数据表示方法上,一旦选错数据结构,会在将来带来严重的影响。

下面这个例子可以表明这种设计错误:选择一个简单的堆栈或队列,而不是循环队列。堆栈就好比是一堆盘子。你取下底部盘子,然后放上另一只盘子,再取下另一只盘子,依次类推。

如果你想拿走一只盘子,你从最上面拿走。这就叫后进先出。可问题,如果你需要拿走早些时候放的东西,就很麻烦了。假设一堆栈盘子有10只盘子。想找到第一只盘子,你就得先拿走其他所有的盘子。

现在,不妨想象一下队列。如果你在银行排队,就处于队列中。第一个进去的也是第一个出来的。一旦第一个人得到了服务,下一个人跟上,然后该人得到服务。出现的另一种情况是,每个人都向前迈一步,在队列中向前移动位置。

要是许多人来排队,会出现什么情况?要么他们被拒之门外,要么队伍排到门外面。第一个人叫到后,所有这些人都要往前移动。

如果你有大量数据,这种队列就会极其低效。每当从队列的开头获取数据,所有数据都要移动。我们置身于大数据时代,有源源不断的数据从我们的系统通过。

在这种环境下,一种更好的办法也许是实施循环队列。在这种情况下,数据根本不动。相反,设置的指针指向队列的开头和末尾;在内部,队列首尾相连,那样数据以圆环、而不是队列的方式来加以组织。当数据元素使用、从圆环中移除后,就不需要移动圆环中的所有数据。发生的只是第一个元素的指针指向圆环中的新元素。

有许多例子可以表明选择正确的数据结构会给你代码的效率带来重大影响,这仅仅是其中一个。

但愿你在看完本文后,会成为一名更高效的程序员,避免其中一些严重错误。

原文标题:Software bugs? Avoid these 10 costly programming mistakes,作者:David Gewirtz

【51CTO译稿,合作站点转载请注明原文译者和出处为51CTO.com】

【责任编辑:张书情 TEL:(010)68476606】

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址