第14章: 组织直线型代码
原则性问题:
- 不要让代码产生隐式的依赖, 比如A函数完成业务的同时还初始化了成员变量, 这样其他成员函数就必须依赖A函数的执行. 正确做法是再单独写一个init函数.
- 就近原则: 把相关的操作放在一起
第15章: 使用条件语句
原则性问题:
- if/else 或switch/case 中将正常情况放在前面, 将异常处理放在后面. 将频率最高的情况放在前面
一些小的点:
- case语句中的代码尽量简短, 如果内容多可以写成一个子函数.
- 要用default处理真正的默认情况, 而不是"最后一种可能"
- 对于多个if/else if/else 的结构, 使用布尔变量使判断条件更为清晰.
第16章: 控制循环
原则性问题:
- 尽量避免循环过长, 如果确实很复杂, 写成子函数
一些小的点:
- 不用将非控制语句写在for的头部
- 如果想在for头部写多条语句, 应该使用while
- 不要修改下标
- 多层嵌套时, 给下标取更有意义的名字, 而不是 i, j, k
- 使用带退出的while, 来避免重复代码, 例如
p = p->next; while(p != NULL) { p = p->next; } 改成: while(true) { p = p->next; if(p == NULL) { break; } }
第17章: 不常见的控制结构
一些小的点:
- 递归应该考虑栈的容量问题, 避免在递归函数中声明较大的局部变量(放在heap里)
- goto是一种信仰, 如果你知道goto的优缺点和替代方案, 仍然选择使用, 无可厚非.
第18章: 表驱动法
当遇到一个逻辑繁杂的问题时, 例如需要考虑多种可能, 每种可能都有不同的处理方式, 使用表驱动是一个非常不错的方法,优点:
- 优雅: 代码量更短, 更有规律
- 可读性: 复杂的逻辑一般连作者都很难读懂
- 减少出错: 复杂冗长的逻辑判断经常会忘记某种情况
- 易于修改: 通常能将复杂的业务修改抽象成表下标的修改, 并集中在一处(预定义表)
定义: 预先用一个查询表(数组, 可以是多维)存储所有可能的情况, 运行时用直接查表取得结果来代替复杂的逻辑判断(一般是很复杂的if).
使用表驱动法需要解决两个问题:
- 如何从表中查询条目
- 直接访问: 将所有可能的情况存入表中
- 优点: 简单
- 缺点: 内存大, 当key值很稀疏时浪费大量内存. 要求key必须能用整数或枚举表示(因为数组下标只能是整数)
- 映射: 将可能的情况通过一个函数/宏映射成表的下标
- 优点: 节省内存, 更灵活
- 缺点: 对于简单问题不必要
- 阶梯: 当表的key值为一个范围时使用, 通过一串if判断实现
- 对key值为浮点数范围时比较好用, 一般都能用映射法代替.
- 如果范围比较多, 可以使用二分法提高查找性能
- 直接访问: 将所有可能的情况存入表中
- 表中存什么
- 直接存数据
- 存动作(action), 如函数指针(C) 或 派生类(C++)
第19章: 一般控制问题
原则性问题:
- 复杂度是衡量一个程序可读性/可维护性的重要标准, 对应的是读懂代码所需要同时考虑的智力实体的数量. 一般来说, 可以通过设计控制结构, 拆分出子程序, 更好的设计等方法降低复杂度, 这是提高代码质量的核心问题.
- 如果if 嵌套过深(大于4层), 一定要想办法简化, 即使有所代价
一些小的点:
- 判断尽量按数轴顺序, 如 (1 <= i && i <= 8), 比 (i >= 1 && i<= 8) 更易读
- 尽量编写肯定形式的布尔表达式, 这样更接近人类的思维(更易懂)