这次准备连续写三章的读书笔记,因为这三章讲得都是很基础但又容易让人忽略的细节问题。
第14章 组织直线型代码
14.1 必须要有明确顺序的语句。
设法组织代码,使得依赖关系变得非常明显。要非常明显,非常是重点。例如下面的子程序就是具备明显的依赖关系:
InitExpenseData(), ComputeExpenseData(), PrintComputeDataResult();后面的一个子程序总是需要依赖前面的子程序。
14.2 顺序无关的语句
使代码易于自上而下地阅读。言下之意,就是要让别人能够按照常规逻辑顺序阅读你的代码。比如下面的代码:


marketData.ComputeQuarterly();
marketData.ComputeAnnual();
marketData.Print();
SalesData salesData;
salesData.ComputeQuarterly();
salesData.ComputeAnnual();
salesData.Print();
把相关的语句组织在一起。如果按照这种规定来编写代码,那么你的代码看起来就一个代码块非常明显地区别于其他代码块。
第15章 使用条件语句
这一章看似根本没什么可以讲,可是相信里面讲得东西你不一定都能做到。
15.1 if语句
首先写正常代码路径,再处理不常见情况。
把正常情况的处理放在if后面不要放在else后面。可是我有时候就颠倒过来了,因为一般错误处理情况代码比较少,我放在前面,看起来自己觉得比较舒服。可是如果遇到嵌套的if,那就必须把正常情况放在if后面,因为这样看起来逻辑性会比较强,可读性也比较强。如:


{
// do something
if (status == Status_success)
{
// do something
if (status == Status_success)
{
// do something
}
else
{
// error control
}
}
else
{
// error control
}
}
else
{
// error control
}
if-then-else 语句
利用布尔函数调用简化复杂的检测。这点稍微有点经验的人都知道,当你的if-else语句判断需要做大量工作的时候,把判断代码抽象成一个布尔函数来调用,并把最常见的情况放在最前面,确保所有情况都考虑到了。
15.2 case语句
为case选择最有效的顺序。可以按字母、数字、执行频率来排列顺序。
不要为了使用case而可以制造一个变量。东西都是如此,太刻意的结果往往是不好的结果。该用case则用case,不该用case就用if-else语句。
把default语句只用于检查默认情况。有多少人写switch-case不把default加进去?反正我是看过好几次了,应该利用default来检测错误,除非你真的考虑全面确定不会出现意外情况了,你确定??所以一般还是老老实实把default写进去来检测错误。
另外,避免复杂的case嵌套,别让自己费脑子,更别让维护你代码的人有撞墙的冲动。
第16章 控制循环
16.1 选择循环的种类
什么时候使用带退出的循环?如果把循环条件检测放在循环开始或者结束处,那就需要写出一个半循环的代码。
看看下面重复的代码:


GetNextRating(&ratingIncrement);
rating = rating + ratingIncrement;
while (score < targetScore)
{
GetNextScore(&ScoreIncrement);
score= score+ ScoreIncrement;
GetNextRating(&ratingIncrement); // 重复代码
rating = rating + ratingIncrement;
}
接下里使用半循环改写,让代码易于维护:


while (true)
{
GetNextRating(&ratingIncrement);
rating = rating + ratingIncrement;
if (score < targetScore)
{
break;
}
GetNextScore(&ScoreIncrement);
score= score+ ScoreIncrement;
}
16.2 循环控制
避免循环出现错误发生的两种方法:一是减少能影响循环各种因素的数量。也就是说,你的循环应该足够的简单。二是把循环内部看作一个子程序,把控制尽量放到循环体外,把循环执行条件表达清楚。即:将循环看作一个黑盒子,外面的程序不知道循环里面做了什么,只知道循环的控制条件,这是最理想的情况。
进入循环:
1. 只从一个位置进入。一般人都会这样做,除非你嫌你的程序太简单,搞一个goto进入到循环里;
2. 把初始化代码放在循环前面。呃,这点和我做得不同,我一般是用到变量之前才进行初始化。但是书中谈到要按照就近原则,把相关代码放在一起;
3. 用while(true)表示无限循环,或者for( ; ; )也可以;
4. 在适当情况多使用for循环。我也是倾向于for,因为for循环给人的感觉比较清晰,不容易出错。
处理好循环:
1. 用'{', '}'把循环语句括起来;
2. 避免空循环。这点我倒是没注意,有时候就写了空循环,如下面:
{
;
}
实际上循环控制中包括了两条语句,为了让代码更加清晰,可写成:
{
ch = getchar();
} while (ch != EOF);
一个循环只做一件事:
符合UNIX的哲学:一个工具只做一件事,并且做好。
退出循环:
使循环条件看起来很明显。
不要为了终止循环而胡乱改动for循环的下标。有些程序员为了贪图方便是会写出这样的代码来的。比如下面:
{
if ( ... )
i = 100;
// ...
}
一旦你写了for循环,循环计数就不是你能控制的。如果你想对循环更多的控制,考虑改用while。
避免出现依赖于循环下标最终取值的代码。我犯过这样的错误,也是为了贪图方便:


{
if ( entry[recordCount] == testValue )
break;
}
if (recordCount < MAX_RECORDS)
return true;
else
return false;
这里通过recordCount来检测循环是否执行到最后面一次了。这样的代码也可理解,但是阅读你代码的人并一定能很快地理解你的代码。考虑使用一个标志:
for (recordCount = 0; recordCount < MAX_RECORDS; ++recordCount)
{
if (entry[recordCount] == testValue)
{
found = true;
break;
}
}
使用循环变量:
在嵌套循环中使用有意义的变量名来提高可读性。太多的程序都是用i, j, k这样的变量来写了,我之前也曾这样写,上一个项目开始就没这样写过了,取个好的名字总是能让代码看起来清爽很多。
把循环下标变量的作用域限制在本循环内。也就说,循环下标在这个循环内用到后,在循环外面都没再用到了。例如上面那个利用循环下标来判断是否找到了值的例子。
16.3 轻松创建循环——由内而外
其实我们大部分人对循环的思考还是这种由内而外的思维方式的,即先想这循环内要做什么事,具体哪些,逻辑顺序排好了之后剩下的只是在外面加一个for或者while了。
END