扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
作者:Jeff Vogel 来源:CSDN 2007年9月24日
关键字:
在本页阅读全文(共2页)
顺便说一下,您可能已经注意到,我没有为上述值做任何注释,这是因为从变量名可以很明显地看出这些值的意义。这正是接下来我要讨论的内容。
提示 3:不要使用弄巧成拙的变量名。
总的目标很简单:编写代码以便让那些不知道其用意的人能读懂,让知道其用意的人能尽快地理解。
实现这一目标最好的策略是为变量、过程等赋以含义鲜明的名字。当他人看到这个变量名时,就会立刻清楚其意义,您也不必搜索整个程序来寻找 incremeter_side_blerfm 的用意何在,这大约会节省五分钟左右的时间。
这里需要进行一些均衡。所给出的命名应该尽量长且足够清晰以便您能理解其含义,但也不能过长或太过怪异,如果这样,代码的可读性就会受到影响。
例如,在实际中,我可能不会像上一节所示的那样给常量命名。我之前之所以这么做是为了让读者在没有任何上下文的情况下也能充分理解这些常量的含义。在程序本身的上下文中,与如下所示的相比:
#define MAX_Aliens_ON_SCREEN_AT_ONCE 5
我会毫不犹豫地这样编码:
#define MAX_NUM_Aliens 5
这个简短的名字所引起的疑惑很快就会迎刃而解,而简短的命名还会增加代码的可读性。
现在来看看在本文中我经常要调用的那个用来将外星人在屏幕上到处移动的代码片段,我会毫不犹豫地这样编码:
// move all the Aliens
for (short i = 0; I < MAX_NUM_Aliens; i++)
if (Aliens[i].exists()) // this alien currently exist?
Aliens[i].move_it();
请注意,包含所有外星人的这个数组的名称很简单,叫做 Aliens。这很棒。它恰好就是我想要的那种描述性名称,这个名称又很简短,即使键入千遍之多,我也不会感到烦闷。此数组将会经常用到。如果将其命名为类似 all_Aliens_currently_on_screen 这样的名称,那么所编写的最终代码将会长出很多,而且代码还会因此变得不怎么清晰。
同样,我还将循环变量直接命名为 i,无任何额外的说明。若是初次接触描述性变量名这个概念,您很可能会忍不住将此循环变量命名为 "counter" 之类的名字。实际上,没有必要这么做。命名变量的意义在于让读者能够立即理解该变量的用意。人人都知道 "i"、"j" 这类名称常常用于循环变量,所以将循环变量如此命名是完全可以的,无需多加解释和说明。
当然,有关变量命名还是需要多加注意。比如,有一种称为 Hungarian Notation 的东西。其种类很多,但基本的理念是在变量名的开始添加一个标记以表示其类型(例如,所有无符号长型变量都以 ul 开头)。这比我希望的要多少麻烦一些,但这个概念必须要了解。为了弄清楚事情可能需要花费太多时间,但还是值得的。
提示 4:进行错误检查。
一个正常大小的程序往往都会有大量的函数和过程。而且更为麻烦的是,其中的每一个都需要进行稍许错误检查。
当创建过程/函数时,应该总要考虑这样的一个问题:“假如一些怀有恶意的人故意向函数或过程传递进各种怪异的值,这段刚刚创建的代码如何能自保并且让计算机也能免受破坏呢?”然后,编写代码来检查这些恶意数据以保护自身免受这些数据的破坏。
举个例子。我们的这个太空游戏的主要目标是杀掉外星人并积分,所以我们需要一个过程来更改分数。而且,当加分时,我们需要调用一个例程来实现分数上星光闪烁的效果。如下所示的是第一个过程:
Void change_score(short num_points)
{
score += num_points;
make_sparkles_on_score();
}
到目前为止还不错。现在请思考一下:这里可能出现的错误是什么呢?
首先,一个很明显的问题是:如果 num_points 是负值该如何呢?我们能让玩家的分数降低么?就算我们能降低分数,但在我之前给出的关于该游戏的描述中,没有提到过失分。而且,游戏应该有趣,但失分无论如何不能算是一个有趣的事情。所以,我们将分数负值视为一个错误并必须要捕获。
上述错误相对容易,但这里有一个很微妙的问题(也是我在游戏中经常要处理的)。如果 num_points 为零又会怎么样呢?
这是一个很似是而非的情景。还记得么,我们会在每个 wave 结束时根据玩家完成速度的快慢给一个奖励分数。如果玩家速度极慢,我们是否应该给他一个值为零的奖励分数呢?在凌晨三点,调用 change_score 并传递值 0,这完全可行。
现在的问题是我们可能不想让计分板在显示的数值没有变化时仍旧五颜六色地闪个不停。所以我们要先捕获这个问题。让我们尝试如下代码:
Void change_score(short num_points)
{
if (num_points < 0)
{
// maybe some error message
return;
}
score += num_points;
if (num_points > 0)
make_sparkles_on_score();
}
好了,情况好多了。
请注意这是很简单的一个函数。里面并没有用到任何极受新手推崇的新奇指针。如果要传递数组或指针,那么最好小心错误和坏数据的出现。
这样做的好处并不仅仅限于让程序免遭破坏。好的错误检查还能让调试更为迅速。假设,您知道写入的数据超出了数组的范围,为了发现可能出现的错误,您需要详细检查代码。若所查看的这个过程中的错误检查均已就绪,那么就无需花很多时间去专门通查它来寻找错误。
这种做法将节省大量时间,而且还能重复。还是那句话,时间是我们所拥有的宝贵资源。
提示 5:“不成熟的优化是麻烦的根源” —— Donald Knuth
上述格言非我个人所造,它可以在 Wikipedia 中找到,所以必定是十分睿智的。
除非是想找别人麻烦,否则编写代码的首要目标就是简明性。简单的代码更易于编写、易于日后理解,也更易于调试。
优化与简明性是相悖的。但有时,却必须要进行优化,在游戏中尤其如此。这个问题至关重要,您可能直到用解析器实际对工作代码进行测试时才会意识到需要进行优化。(解析器 是一种程序,用来监视其他程序并找出该程序使用不同的调用所花费的时间。这些都是很棒的程序。您可以找一个来试试。)
每次当我优化游戏时,常常都禁不住会大出所料。我十分担心的那些代码总是问题不大,相反,我觉得万无一失的代码反倒会运行得十分缓慢。由于我对运行速度的快慢并没有什么概念,在获得实际数据之前我所进行的优化根本就是浪费时间。比浪费时间更糟糕的是它还让代码变得有些混乱。
这个规则看来很难遵守。但,如果规则很容易,它也就称不上规则了。好的程序员大都更痛恨将原本可以运行迅速的代码弄得臃肿笨拙。
但好消息是,在我不断 “该这样不该那样的” 布道式的介绍中, 这是惟一的一个您可以稍微懈怠一些的地方!
请让自己编写的代码尽量整洁和有效一些吧。在后面的优化阶段,可能需要将其变得面目全非。所以如非必要,请慎重。
说到伤害,接下来,就来看看最后的这条建议。
提示 6:不要一知半解、自作聪明。
您可能听说过 IOCCC 吧,即 International Obfuscated C Code Contest。大家都知道,C 和 C++,不管其优势如何卓越,都会最终导致编写的代码噩梦般地复杂。这个比赛就是要通过评选出最离谱的代码来展示简明代码的价值,真是别具匠心。
让我们来看看在您自认为具有了编程的全部知识并甘愿冒险的情况下,您能制造什么样的麻烦。足够的知识让您信心百倍地将十行代码压缩进一行代码内。付出的代价就是您绝对无法快速修复其中可能存在的 bug。
这里所需吸取的教训就是如果您所编写的代码要求您必须具有有关复杂优先规则的详细知识或让您不得不翻看某些书的后面章节才能弄清来龙去脉,那么您在编写这段代码时就犯了一知半解、自作聪明的毛病了。
每个人对代码的复杂性都有自己的容忍程度。就我个人而言,我编写的程序往往呈比较典型的保守风格。我个人认为,如果一段 C 代码需要您必须知道 i++ 和 ++i 之间的差别,那么这段代码就过于复杂了。
您尽可以把我想象成一个循规蹈矩的人。没错,我的确如此。但循规蹈矩却可以让我花很少的时间就可以读懂我的代码。
结束语
分享这篇文章......
至此,您可能会想:“哇哦,真是浪费时间。您介绍的所有这些东西都是显而易见,尽人皆知的。为何还多此一举,写这样的文章呢?” 实际上,我很希望您会这么想,因为这意味着您已经进步了,变得明智了。这很好。
但不要错认为所有这些内容对每个人都是不言自明的。事实并非如此。糟糕的代码随处可见,但实际上这些代码本不应如此。
如果您正在努力编写大量代码并想让自己不受其所累。那么就请让代码尽量简单明了一些,这样,您就可以节省大量时间和免受很多挫折。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1772115
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者