如何保证代码质量

本文最后更新于:2022年8月17日 凌晨

当下,小到手腕上的手环,大到运载火箭,都依赖于使用代码描述的逻辑而运转。代码质量关系着生活的方方面面,如果一段质量不佳的代码被用到了关键位置,就有可能带来严重后果和经济损失,比如阿丽亚娜5运载火箭在首次测试发射时就因此发生事故。

因此,保证代码质量是一件重要的事情,高质量的代码才能带来优秀可靠的项目。

什么是优秀的代码

优秀的代码不少见,它们虽然各不相同,但总有一些共同的优点:

  • 函数、变量具备明确的语义,甚至不需要注释很容易就能看懂其作用
  • 满足各种边界条件,代码功能正确
  • 在功能变更时只需要进行较小的改动,具备较好的拓展性
  • 在发生异常时能够优雅的报错,或者能够自我恢复
  • 对于未预见的情景,能够优雅处理而不崩溃
  • 代码安全度高,不易被攻击

代码的可维护性原则

面对一个庞大的项目,如果代码缺乏可维护性,那么项目未来的发展将会异常艰难(比如一个Bug光是定位问题位置可能就需要非常久的时间),而对项目的更新迭代也将如噩梦般痛苦(面对一大坨不知所云的代码不知所措)。

代码的可维护性原则包括下面几点:

  • 统一的编码规范
    • 包括命名规范、代码格式、注释规范...
    • 比如:不要在命名中混合拼音和英文
  • 稳定的工程结构
    • 目录清晰(可以轻松找到相关代码)、模块化、组件化、依赖可控(依赖项容易添加和更新,基本保持稳定)、访问权限(防止代码被无意中破坏)...
  • 优秀的方案实现
    • 文档、用例(对应用场景考虑充分)、可测性(自动化测试)、高内聚低耦合...

代码清晰/复杂程度的度量方法

为了方便度量代码的清晰程度,这里介绍一下圈复杂度(Cyclomatic Complexity,也叫条件复杂度)的计算方法。

根据代码得到CFG(控制流图,Control Flow Graph,是一个过程或程序的抽象表现,是用在编译器中的一个抽象数据结构,由编译器在内部维护,代表了一个程序执行过程中会遍历到的所有路径),则这段代码的圈复杂度=CFG边数-CFG顶点数+2。另一种计算方法对人类更加友好,只需要统计代码中判定条件的数量,圈复杂度=判定条件数+1,这两种计算方法是等价的。

有了圈复杂度,我们就可以对代码进行评估了,通常来说,如果圈复杂度为1~10,那么代码是清晰的,维护成本也较低;而如果圈复杂度大于30,那么代码就基本不可读,维护成本也非常高。在实际应用中,单个文件的圈复杂度最好不要超过15。

代码评审 Code Review

代码质量关系着项目的未来发展和维护,那么为了保持代码的质量,就需要对代码进行评审。代码评审(Code Review,CR)是通过阅读源代码,检查代码是否符合编码规范以及前置发现代码质量问题。

CR的关注点

CR需要重点关注:

  1. 代码规范
    • 命名:变量名、函数名、类名是否清晰准确
    • 描述:提交的描述是否准确对应到功能
    • 风格:代码风格是否遵循统一规范
    • 文档:是否提交/变更了相应的文档
  2. 功能设计
    • 设计:设计是否良好、是否适合当前系统
    • 功能:实现是否正确
    • 简洁:实现是否简洁,有无更好的实现方式
    • 测试:是否包含自动化测试,代码可测性如何

小CL(Change List)的好处

前面介绍了CR是什么以及CR关注什么,下面来介绍小CL的好处。小CL是指提交进行Code Review的Change List要尽可能小,把一大段代码拆小再提交给CR。这样做的好处是审查将会更快(代码片段小,不需要太长时间)、更彻底(代码少,更容易看懂),更容易合入代码(合并时不会产生大的冲突),此外如果未能通过代码审查而被拒绝,也能快速修改。

如何面对CR的不同意见

  1. 正视批评
    • 当审查者对代码提出批评时,应当正视批评,不要为批评而倍感沮丧
  2. 修复代码
    • 如果审查者对代码的某一部分产生误解,应当及时澄清代码并加上注释
  3. 自我反思
    • 思考自己的解决方案是否合理,是否有更佳的解决方案
  4. 解决冲突
    • 尝试在基于技术事实和业界标准的基础上与审查者达成一致

代码重构

在很多时候,我们可能无法在项目的一开始就确定最佳的架构、最优的技术,可能会因为种种原因而导致代码质量没有那么高,也就是产生了所谓技术债(Technical Debt),给未来的项目维护带来了负担。常见的技术债产生原因有时间紧迫、技术水平不够、业务压力大等。既然是技术债,总归是要偿还的,解决技术债的一种方法就是对代码进行重构,以提升代码的可维护性。

代码重构是指对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。

何时重构

重构,其实就是为了提高代码质量,不断对抗所谓的Code Smell(代码的坏味道)。因此,当出现Code Smell时,就应该进行代码重构了。常见的Code Smell有:

  • Duplicated Code 重复代码
  • Dead Code 死代码,代码没有什么用,但是却没有被移除
  • Long Method 冗长的方法体
  • Lazy Class
  • Feature Envy
  • Divergent Change
  • Refused Bequest
  • Shotgun Surgery
  • Inappropriate Intimacy
  • ......

常见的代码重构方法

  • 对于重复代码,可以抽离出公共代码,进行类型一般化(从几个类中抽取出公共部分作为这几个类的父类)
  • 对于参数过多的函数,可以将参数封装,通过函数调用获得参数(比如将几个关系较近的参数放在一个类中,传参时传入该类的对象,函数通过调用相关方法获得参数)
  • 对于过于冗长的函数,可以从中抽取出多个子函数,而不是把所有代码堆在一个函数中
  • 对于过于庞大的Switch-Case语句,可以通过策略模式、类型抽象来重构(把Case抽象为一个个类,并为这些类抽象出同一个方法,各个类通过重写方法来实现Case相关的代码,这样只需要调用这个方法而不是写一堆Case语句)
  • 对于嵌套的条件分支,可以提取方法来降低圈复杂度(把嵌套的条件取出来)
  • 对于长调用链(比如a.b().c().d()),可以通过隐藏中间人调用来解决(比如把d()直接提取到a中,实现a.d()
  • ......

如何保证代码质量
https://young-cloud-creator.github.io/code-quality/
作者
Young Cloud Creator
发布于
2022年8月15日
许可协议