《程序设计箴言》 |
这是一种神奇的物质,它相对静止且独自守候,然而又处于永恒的运动之中。
它是所有程序的源头,我不知道它的名字,所以我将称它为编程之道……
-----------------------------------------
1、Include precise preconditions and postconditions with every function that you write.
你编写的每一个函数都应该包含准确的执行先决条件和后置条件。
每一个函数都应该带有一个说明文档,它由函数的先决条件和后置条件组成,有时还包括该函数所调用函数的函数名列表。所谓先决条件就是函数被调用前必须做的一些初始化工作以及各种形式返回的变量的含义;而后置条件则包含函数的功能和通过函数调用后以各种形式返回的变量的含义。编写函数说明文档的意义不仅在于阅读程序时,可以迅速地对函数有一个大体的了解,更重要的是它能帮助我们避免两个函数间的接口错误。
2、Always name your variables and functions with the greatest care,and explain them thoroughly.
要十分慎重地给你的变量和函数命名,使其完整准确地解释它的功用。
一个程序能够正常工作,准确的掌握每个变量的含义和每个函数的功能是非常重要的。文档固然能起解释的作用,但一个恰如其分的名字却能事半功倍。以下几条建议或许会有所帮助:
a.全局变量用具有描述意义的名字。
b.局部变量(特别是循环变量)用短名字。
c.函数采用动作性的名字。
d.保持命名的一致性。
e.同一变量名不要有多种含义。
f.不要用过于相似的变量名。
g.变量名中一般不要带有数字。
h.规模较大的程序使用匈牙利表示法。
i.显式地说明变量。
j.对变量最好做出注释说明其含义。
3、Keep your documentation concise but descriptive.
保持你的文档简洁,但有较强的描述性。
一个好的文档通常做如下工作:
a.像箴言1建议的那样给函数作注释。
b.每当声明一个变量、常量、类型时,解释它是什么以及它怎么使用,最好能做到“望文知义”。
c.程序中的每一个模块都应该以一段简明的说明其功能和操作的注释开始。
d.对程序中没有明显结束标志的部分做标记。
e.不要大谈明显的东西。
f.对每一个使用了某些小技巧或是含义不明确的语句作注释,更好的做法是避免这种语句的出现。
g.代码本身应该解释程序是如何运作的,文档应该用来解释做了什么和为什么这样做。
h.无论何时,当代码改变了,一定要让文档也随之改变。
i.注释不要与代码矛盾。
j.不要注释不好的代码,而应该重写。
4、The reading time for programs is much more than the writing time. Make reading easy to do.
阅读程序的时间要比编写程序的时间长得多,所以要保证它的易读性。
记住下面几条,会对你提高程序的易读性有所帮助:
a.使用首行缩进,以表现出各语句逻辑上的层次关系。
b.使用表达式的自然形式。
c.尽量使用括号以排除歧义,不要假定读者熟记运算符的优先级。
d.分解复杂的表达式。
e.使用字符形式的常量,不要用整数。
5、Don't lose sight of the forest for its trees.
不要为了几棵树而放弃整片森林。(不要因小失大。)
解决任何问题时都应该采用自顶向下,逐步细化的策略。根据对问题的整体分析,先写出主函数,而使各个子问题以函数的形式存在于其中,然后明确各个函数的功能,各个击破。
6、Each function should do only one task, but do it well.
每个函数只须执行一个任务,但要把它做好。
这条箴言从实现的角度阐明了函数应该具有强内聚性的原则。所谓内聚性是指一个子程序中各种操作之间互相联系的紧密程度。强内聚性的目的是使函数的功能尽可能的单一,以提高子程序的可靠性。
7、Each function should hide something.
每个函数都应有封装性。
封装性大大降低了程序的复杂性,一旦你完成了某个函数,就可不必再考虑其内部工作细节,只要调用它就可以了。
8、Never cause side effects if you can avoid it. If you must use global variables as input, document them thoroughly.
如果可能,尽量避免副作用。如果你必须使用全局变量作为输入,那么在文件中透彻地做出说明。
函数中的数据类型有五种:输入变量(形参)、输出变量、输入输出变量(变参)、局部变量、全局变量。函数间的数据传递应该使用输入变量、输出变量和输入输出变量,而应该避免使用全局变量。使用全局变量不仅会使函数间的关系变得复杂,有时还会产生更糟糕的副作用。
9、Keep your input and output as separate functions, so they can be changed easily and can be custom-tailored to you computing system.
程序的输入和输出应改作为单独的函数而存在。这样他们容易修改,并且可以根据你的计算系统定制。
程序的输入必须被严格的检测,以保证其合法性和适应性。而输出必须被仔细的安排和格式化,并提供不同的选择以适应多环境。故输入和输出必须被有效地组织以保证程序的正常工作。
10、The quality of test data is more important than its quantity.
测试数据的质量比它们的数量重要得多。
程序完成后就应该对其测试。测试是在你认为程序能正常工作的情况下为设法打败它而进行的一系统化的实验。
测试的工具是测试数据。所以测试数据的选择对测试效果有非常大的影响。测试方法一般采用黑盒方法和白盒方法。一个较好的建议是对一项大工程进行测试时,对其每一个基准单位运用白盒测试,对其每个规模较大的模块甚至整个工程实行黑盒测试。
11、Program testing can be used to show the presence of bugs, but never their absence.
程序测试只能找出错误,却不能证明他们不存在。
这是Dijkstra的名言。要对一个程序进行测试,测遍所有数据或试遍所有路径是最可靠的。这是黑盒方法和白盒方法的两种极端情况,但事实上很难做到这一点。所以测试点的通过只能增强我们对程序的信心,并不证明他们不存在。
12、Most programs spend 90 percent of their time doing 10 percent of their instructions. Find this 10 percent, and concentrate you efforts for efficiency there.
大多数程序花费90%的时间来执行10%的代码。找到这10%,集中力量提高其效率。
每个程序都有自己的核心代码,这段代码往往会在整个程序运行时被频繁的执行,基执行效率对整个程序来说是至关重要的。所以提高其效率是最有效的。要使它执行速度快,最重要的因素是算法与数据结构的选择。
13、Never code until the specifications are precise and complete.
一定要在有了准确完整的设计书后再进行编码。
一项大的软件工程在合适的时间进行编码是重要的,不能太早也不能太晚。大多数程序员错误的过早进行编码。如果在设计书明确前进行编码,那么编码时就有可能对设计书做出一些不当的假设,或是拖延编码时间。我们应该自顶向下地进行编码,并逐级进行子程序设计和测试。
14、Keep your algorithms as simple as you can. When in doubt, choose the simple way.
尽可能使你的算法简单,如果没把握,那么选择简单的方法。
选择简单方法的原因有两个:从程序员的角度来看,实现一个复杂的算法往往比实现一个简单的算法要花费更多的时间和精力,而有时这种花费是得不偿失的。另外,从存储花费上看,一个复杂的算法要在效率上有所作为,往往是以牺牲存储空间为代价的。
15、Consider time and space trade-offs in deciding on your algorithm.
在决定你的算法时要权衡时间和空间的花费。
时间和空间常常是算法代价矛盾的双方,如何取舍往往取决于程序运行的环境。如果有足够的可用存储空间,那么时间应该作为考虑的主要因素。反之则优先考虑空间。虽然在科技日新月异的今天,计算机性能大幅提高、存储器相对廉价,但这种矛盾在某种程度上依然存在。所以,慎重对待时—空矛盾在今天仍有意义。
16、Act in haste and repent at leisure. Program in haste and debug forever.
匆忙行事,不悔难求,草率程序,调试不休。
软件工程中提出的“软件生命周期”是一个很好的借鉴,无论你面对的是何种规模的程序都有借鉴价值:
a.准确而完整的分析问题,一定发仔细地确定所有必要的用户接口。
b.建立一个原则,并在其上实验,直到所有的设计确认。
c.设计算法,尽量使用数据结构提供的工具或其它算法的现在函数。
d.确定算法的正确性,或使算法足够简单以至于其正确性是不言自明的。
e.通过分析算法,得出其需求,并保证其与设计书相吻合。
f.用合适的程序设计语言进行编码。
h.用精心挑选的测试数据对程序进行测试。
i.对所有的子程序细化、重复上述步骤,直到软件完成并且功能完善。
j.如果需要,优化代码以提高其性能。
k.维护程序,使其满足不断改变的用户需要。
17、Starting afresh is usually easier than patching an old program.
从头重做通常比修补一个旧程序要容易得多。
修补一个旧程序是困难的,首先你要看懂它,找到当时创造时的思路。而如果程序没有良好的注释和编程风格那这第一步就很难完成。所以,一旦你要改变10%以上的代码时,你应该重写它。
18、Always plan to build a prototype and throw it away.You'll do so whether you plan to or not.
无论计划与否,你总是先建立一个原型,然后再将其抛弃。
这是避免程序重写的必要方法。
19、Practice information hiding:Separate the application of data structures from their implementation.
使用信息隐藏:将数据结构的应用与它的执行分开考虑。
将顶层运算和底层运算分开考虑,这是分治思想的一个体现,便于我们集中力量,提高效率。
为实现这一目标,必须对二者的接口进行一次抽象,让底层只能通过这个接口为顶层服务,顶层也只能通过这个接口调用底层的运算。而这个接口就是《数据结构》课程研究的重点:ADT4。
20、Once your data are fully structured,your algorithms should almost write themselves.
数据善置,算法自成。
前人在算法上已经作了不少工作,数据结构定了,相应或类似的算法自然会找到。
所以,遇到问题能够全面的认识并建立合理的模型,那么你已成功了一大半!!!