2022.10代码大全阅读心得2

第十四章 组织直线型代码

14.1 必须有明确顺序的代码

对于具有明显的顺序关系的代码,应该使用顺序结构。

对于隐含的顺序关系,应该:

  • 去除不合理的依赖关系(如不应该在CalculateExpense里对某个变量进行初始化)
  • 将依赖关系明确体现在__子程序名__或__子程序参数__上。
  • 将依赖关系体现在__注释__中。
  • 用assert或错误处理代码检查依赖关系。

就个人看来,这仍然是强调解耦合的问题。

14.2 顺序无关的代码

对于没有顺序关系的代码,应该通过排列代码增加代码的可读性。
应该将相关的代码组织在一起,从而便于自上而下阅读。
组织较好的代码应该可以划分成若干个不重叠(但是可能嵌套)的代码块,各自执行相关的功能。

这一部分让我感触比较深。对于有明确顺序的代码通常我都会注意到将它们排列整齐,但是对于没有顺序关系的代码我就经常将它们散落各处。将代码按照相关性组织在一起确实可以增加代码可读性,而且修改时也方便修改。

而且个人又想到,有时候代码之间的执行顺序其实没有依赖关系,但是还是可以按照逻辑关系排列顺序,比如将getPage(1)放在getPage(2)前面,尽管可能getPage(2)并不依赖getPage(1)的执行。

第十五章 使用条件语句

14.1 if语句

对于简单的if语句,需要注意以下:

  • 应该先处理正常情况,再处理不常见情况,再处理不正常情况。这样可以增加代码可读性。
  • 不要写空的if语句,这很让人困惑。
  • 如果没有else语句,要考虑需不需要假如else语句。必要时可以加入空的else语句。
  • 测试时,各个分支都应该测试。

对于复杂的if-then-else关系,需要注意如下:

  • 可以使用布尔函数简化复杂的检测。这个建议对我是有意义的,不过私以为对于仅出现一两次、功能不是很独立的布尔表达式就算很复杂也没必要用函数简化。可以用布尔变量简化。
  • 把最常见的情况放在前面。
  • 尽量确保所有情况都考虑到了。可以在最后放一个else语句,用于处理错误或者检查没考虑到的情况。
  • 尽量用别的语句替换if-else语句,如switch-case语句。但是个人觉得cpp里面的switch-case语句很丑……if-else语句可读性更强。

这一套规则整体来说大概是在强调if后面的语句比else后面的语句阅读时更自然,所以应该尽可能将正常的处理放在if后面,而且不要将if后面放空语句。

这个规则其实让我有些意外,因为我经常将错误处理放在正常处理前面。经常正确情况需要满足许多苛刻的条件,而需要对这些条件一一检查、一一进行错误处理。这时候将正确处理放在最前面有些为难。是应该在前面检查所有条件,发现出错之后再重新一一检查并进行对应的错误处理么?感觉有点复杂。不过对于简单的情况这个规则是有道理的。

另外尽量加入else语句这个规则让我想起《程序员修炼之道》中也强调过这一点。即使认为else的情况真的不可能发生,那么同样可以加一个报错的else语句。这样可以不忽略任何一个错误。

14.2 case语句

选择有效的排列顺序:按照字母表或数字表排列;将正常状况放在前面;将出现频率高的情况放在前面。
这里除了对于可读性的考虑以外,应该还有尽可能缩短判断次数的考虑。

应该注意:

  • 每个case中执行的操作应该尽可能简单。在cpp中switch-case语句排列起来个人感觉很丑,各个语句都堆积在一起(不过也有可能是我自己写的问题……),一旦语句变多可读性就会变差,而且还很容易忘掉break语句。
  • 不要为了使用switch-case而刻意使用一个变量。这样代码逻辑混乱还容易出错。如果真的需要复杂的条件判断,还是应该用if-then-else。
  • 可以用default处理错误情况。这一点和if-else相通。假如每一种情况都合法,应该用default语句处理真正的默认情况,而不是“最后一种可能性”(这大概也是为了可读性)。

第十六章 控制循环

16.1 选择循环的种类

while循环:每次循环检查一次表达式的值,假如为假则退出。需要重点关注检查在循环开头还是结尾。在cpp中,while在开头检查,do-while在结尾检查。

带退出的循环:在退出循环的条件比较复杂时,可以选择在循环中间退出循环。需要注意:

  • 应该将不同退出循环语句写在一起避免被遗忘。
  • 可以用注释阐述自己的意图。

个人觉得cpp中带退出的循环一个需要注意的地方在于可能会记错究竟是在跳出哪一个循环……

for循环:用于固定次数的循环非常方便。书中建议较复杂的循环应该使用while而非for。个人还经常用for表示复杂的循环的,确实经常会被下标运算困扰到。以后还是将复杂循环用while表示为好。

foreach循环:用于遍历。

16.2 循环控制

循环控制中最容易出现的问题就是控制进入、退出循环的条件出错。应该采取的一个策略是将循环控制与循环中执行的语句相分离,就像将循环中执行的语句作为子程序调用。不过这经常不容易。

进入循环时应该注意:

  • 只从循环头部进入循环,而不要用goto之类的。
  • 将初始化语句写在循环前面很近的地方。
  • 适当地使用for,但不应该什么都塞进for(这个建议对我很实用QWQ)。书中举了两个错误,感觉很典型就摘了过来。在这两种情况下,用while可读性更强,而且显然更符合逻辑:
for(inputFile.moveToStart(), recordCount = 0; !inputFile.endOfFile(); recordCount++){
    inputFile.getRecord();
}

这一段里面recordCount不是控制语句,不应该放在for里面。

for(inputFile.moveToStart(); !inputFile.endOfFile(); inputFile.getRecord()){
    recordCount++;
}

这个控制语句在外部、操作语句在内部,好像没什么问题,但改成while更符合逻辑。

inputFile.moveToStart();
while(!inputFile.endOfFile()){
    inputFile.getRecord();
    recordCount++;
}

对于循环体应该注意:

  • 用{}将循环体括起来。我经常为了省略{}在只有一条语句的时候不使用{},实际上这样很容易出错,因为经常需要扩充循环体的内容。
  • 不要使用空循环。空循环通常是因为操作语句和判断条件的语句都写在了判断条件内,应该将操作语句提出来放在循环体内。这个……我也经常为了省略而这么干,以后我也尽量避免吧。如果真的需要检查某个函数的返回值,也可以用变量表示返回值再在判断条件内判断,而不要将函数调用直接写在判断语句里。不过有时候有的循环是为了延时,空循环是不可避免的。
  • 循环控制语句(如i++)放在循环开头或结尾,总之是与循环操作独立开来。
  • 一个循环只做一件事。同样为了解耦。

退出循环时应该注意:

  • 确认循环是能退出的。
  • 将退出控制放在一处。一定要分散的话,至少要非常明显。
  • 不要为了终止循环乱改for循环下标。这很不利于debug,及后期可能修改for的控制条件。
  • 避免在离开for循环后用循环的最终下标进行条件判断,如:
for(int i = 0; i < MAX_SUM; i++){
	do sth
}
if(i < MAX_SUM){
	do thing1
}
else{
	do thing2
}

这样很容易记混小于号和小于等于号,而且可读性会变差。最好用一个布尔变量表示。
……我也经常为了省略一个变量这么干……

  • 考虑使用安全计数器,一旦循环多于一定次数就退出循环。有时候这很有用,但这会增大代码复杂度,而且不一定有必要。

如果要在中间退出循环:

  • 如果退出循环条件很复杂,使用break,而不要在中间修改布尔变量,再将布尔变量作为while for的条件。
  • 不要将break写得到处都是。个人觉得这两条有一点矛盾,不过既然都是为了简化代码、增加可读性,那么依情况而定,写的最直观的方法就对了。
  • 仅在循环开头使用continue,不要在中间用continue。个人认为continue问题在于,和break一样很容易(甚至更容易)忘记continue的是哪个循环。
  • 语言支持的话使用labelled break增加可读性。

检查循环时,分别检查开始、中间情况和退出。

使用循环变量时应注意:

  • 使用整型表示数组、循环的边界,因为浮点数有时会出现精度不够、循环无法终止的问题。
  • 使用有意义的循环变量名。这与追求简便的习惯不符,不过确实有道理,应该尽量采用。
  • 尽量将循环变量的作用域限制在本循环内。但是cpp中for支持这么做,while却不支持这么做。

循环的长度应该注意:

  • 循环要尽可能短、尽可能一目了然。
  • 嵌套不要太多,应该三层以内。
  • 把长循环的内容移到子程序里面。
  • 如果非用长循环不可,应该让它尽可能清晰。

16.3 轻松创建循环:由内而外

介绍了创建复杂循环的一个方式:先写内部代码,再写循环变量、边界条件,再加入初始化。假如需要嵌套,就层层如此处理。

posted @ 2022-10-30 18:41  -她的梦-  阅读(34)  评论(0编辑  收藏  举报