代码质量基本细则
这篇文章提出一些代码质量的基本准则, 将这些准则融入日常写代码的习惯中,尽量保证在自然意识的状态下写出BUG很少的代码。此准则供约束我个人编程行为使用,也供大家参考。 多读和借鉴优秀的源代码, 不求多写, 但求有意识地克制自己随手写代码的惰性。
推荐书籍: 《代码质量》, 《整洁代码之道》, 《编写可读代码的艺术》,《Writing solid sode》, 《代码大全》
写在前面的话:
写代码不仅仅只是实现指定需求; 多想想所要实现需求的相关联事物,比如关联的外部子系统,关联需求等。
跳出当前视角,尽量从更宏观的角度审视原有代码; 多用设计性的思维来看待和完成编程工作。
写代码不是简单地敲键盘,其间会涉及到很多的微决策,正是这些微决策会慢慢磨练出一个人的思考和决策能力。
准确理解API, 尽可能理解自己所编写的每一行代码, 至少要从代码的语义上确定无误。
总体风格
1. 使用编程语言所推荐的业界编程风格。 比如 Java语言的《Java编程风格》、《Google Java开发规范》; C语言的 K&R 《TCPL》。
2. 保持一致性。 包括缩进、空行、换行、操作符两边的空格、命名等。
3. 写朴实自然的代码。尽量遵循惯用法和大众用法,以及所用编程语言地道的方式。奇淫巧技可以贴在论坛里供大众观摩欣赏,但不宜放在生产环境。
4. 逻辑清晰、简洁、紧凑。写代码就如同说话一样,想清楚了再说。说出口就应该是简洁、有条理、干脆利落的,不要罗嗦,拐弯抹角。
5. 理解和遵循已有规范。仅在有充分理由并与相关人士做好沟通之后少量违例。
命名
作用域
1. 避免使用全局变量,共享变量。
2. 使用时声明和定义,尽可能缩小变量的作用域。
3. 尽量使用私有变量,缩小变量的暴漏范围。
4. 延迟初始化,使用有意义的初始值,该初始值可以作为默认值。
5. 循环变量作用域限定在循环内。
空处理
1. 在操作对象引用或指针时,先进行非空检查。若为空,可以给出提示信息、返回默认值,或者什么都不做。
2. 将对象与原子值比较时,要先对对象判空,避免拆箱时NPE。
3. 获取API结果后,使用之前要对结果对象进行判空,避免NPE。
4. 处理数组或集合前先判断是否为空。
条件处理
1. 尽早 return ; 使用卫述句解耦多层嵌套 if-else 。
2. 使用 map 或 表结构 替换简单的多值 switch 分支。
3. 不要误敲操作符,比如将 && 写成 & 。== 写成 = 。
4. 涉及多个与或非复合条件判断时要小心,正确使用与或操作符; 可以将条件拆解为多个含有业务语义的原子条件然后组合起来更可读。
5. 不确定多个复合操作的优先级时,将操作加括号。
循环遍历
1. StreamAPI > foreach > iterator > for (int index) > while 。
2. 循环遍历小集合时,生成新集合,而不是修改原有集合。
3. 如果遍历的时候需要删除元素, 使用迭代器。
4. 如果要访问索引使用 for 循环时,注意使用半开半闭 0 <= i < len or size ;防止越界。
5. 涉及范围(数组、集合等)的代码要特别注意空值及边界情况。
注释与文档
1. 极少人能写出不需要注释的代码,—— 过三到六个月再看看。
2. 注释简洁扼要。重点说明功能、用途。
3. 涉及特殊处理,重点说明原因。
4. 使用已有算法时写明引用出处; 标注作者信息及联系方式以供交流。
5. 做数据结构转换时,可以通过一个示例说明具体转换行为。
6. 做抽象类时,可以通过一个实现类来说明抽象类的使用。
7. 说明隐含的不易猜测的含义。
8. 提示在特定场景下可能的坑。
函数编写
1. 创建单一职责的短小函数,尽量控制在 60-80 行。
2. 对参数做严格校验,不做任何假设。
3. 参数个数尽量不超过3个。
4. 尽量避免将类型相同的参数排列在一起。
5. 尽量不改变传入的参数,而是将结果返回。
6. 总是习惯创建新的可复用函数,而不是将逻辑直接写在主流程中。
错误处理
1. 区别错误与异常。 参阅《程序健壮性: 正常、错误和异常》。
2. 遵循防御式编程。不能确定的变量,复杂逻辑、IO 操作捕获异常。
3. 调用API后要注意捕获异常。
4. 打印适当的关键日志方便排查问题原因。日志信息带上可识别唯一实体的关键词。
5. 使用正确的日志级别。对于排查有帮助的用info级别,有轻微问题但不影响结果和整体的用warn级别,影响结果和整体的错误用error级别,导致系统崩溃的用fatal级别。尽量避免使用 debug 级别。
6. 捕获所有的异常。底层错误打印异常日志并转译成高层语义,保证高层视图的正确性;给出合理的提示信息及错误源信息。
7. 将所有的错误码和错误消息集中到一个类或一个包下集中管理。
8. 隔离易出错部分, 将复杂、晦涩、容易出错的手工解析代码隔离在系统的小角落范围。
9. 解析JSON串或数值型字符串时要捕获异常,因为难以预料字符串格式是否合法及具体内容。
业务流程与逻辑
1. 写代码前规划一下思路,做一点设计性的思考工作; 约束条件、资源、算法和流程先用草图勾画清楚。
2. 考虑所有的输入,限定合法输入的范围,拒绝非法输入;
3. 保证输出内容正确完整,格式清晰; 若输出依赖于某些变量,要保证当变量修改时能够同步到输出。
4. 分离可变和不可变部分。通用功能提炼成工具箱,业务特定逻辑放在主流程中。
库与工具箱
1. 尽可能使用成熟的库、框架和数据交换格式(例如XML和JSON),而不是手工解析。
2. 遵循标准的约定而不是自定义。比如返回结果<code, message,data, success>
3. 使用成熟的算法; 必要的话,验证所采用计算方法的正确性。
4. 使用网上拷贝的代码时,加上一些优化和单测。
5. 保证传递参数的数目、类型和顺序正确;准确理解 API , 尽量传入合法且有意义的值。
6. 关联变量或方法要注意同步修改。比如 java 的 equals 与 hashCode 方法; 圆类的 边长与面积字段。
7. 尽量避免有潜在问题的隐式类型转换。
测试
1. 建立覆盖性高的单元测试。
2. 建立自动化可重复的接口回归测试。
优化与重构
1. 避免复制/粘贴/修改代码, 尽管很便利;
2. 从业务流程或逻辑中提炼通用功能、业务点,消除重复的代码。
3. 跳出当前视角,思考更清晰的设计与实现方式;
4. 涉及并发的代码要尽可能审慎, 把事情的机理和风险理解清楚了再动手。
5. 防止计算溢出、缓冲溢出、SQL 注入等常见安全问题。
6. 避免大量创建不可变对象,尽量重用对象; 用完后及早回收,预防内存不足和泄漏。