如何提高代码质量

何谓代码质量?

代码是给人看的

1. 书写规范:遵守本身公司制定的编程语言书写规范。
2. 易阅读。
3. 易修改。
4. 易测试。git

代码是给机器运行的

1. 安全
2. 快速
3. 稳定web

代码质量的标准?

对于机器来讲,标准是恒定的,但不可兼得。

好比:编程

  • 锁机制:安全

    • 安全、慢
  • 指针:数据结构

    • 快、不稳定
  • 改内存地址:app

    • 快、不安全

总的来讲,这就像 CAP 理论同样,不一样场景下的需求不同,根据当下业务需求去作出取舍便可。编程语言

对于人来讲,标准是变化的,由于习惯不一样、工期不一样、目的不一样。

易阅读

表意明确

名词要准确

类、结构体、变量、常量等名词要能直观地描述这是个什么东西,通常 1-5 个单词组成为宜。函数

英文单词里没有官方缩写的就尽可能不用缩写,像 result 就 6 个字母,也有人给缩写成 res、ret,temp 缩写成 tmp,更有甚者写 cnt,它究竟是 count 仍是 content 呢?除了歧义,彻底没有任何好处。性能

动词要精简

方法名、函数名等动词要能保证只作一件事。一个方法不写太长的前提是它的功能自己就很少。单元测试

形容词要归约

属性、校验等形容词要归约为方法,业务逻辑关键点大多在判断上,预留扩展点会让阅读难度不随着加需求而快速增大。

单词统一

不要有歧义,由于人是有思惟惯性的,好比一样的业务逻辑,有的写 add,有的写 append,有的写 insert,会严重影响阅读效率。

描述业务

有意义的名字要专一于描述业务,使读者经过阅读代码理解业务逻辑。不要在起名中掺杂数据结构。

例如这么几个场景:列表(集合)、配置映射

  • good case:users(用户集合),articles(文章列表)、siteNameToSiteId(映射)
  • bad case:userSet、articleList、siteMap

加上类型,并不会对理解业务逻辑有帮助,读者看到 list,map 这些关键词还会联想到数据结构中,很容易打断思惟。

避免赘述

具备包含关系或从属关系的时候,不要重复,不要表达累赘的语境。

关注做用域和生命周期

当一个变量的做用域很窄,或生命周期很短的时候,能够用单字母命名,通常来说,单字母意味着临时使用,读者在了解逻辑的时候能够不用关注这部分。

变量要接近使用的地方声明,不要开头声明一堆变量,隔几十行才使用。

写有用的注释

“好的代码是自描述的” 即读代码就和读文章同样。注释不该该用来解释代码逻辑,而应该是用来讲明为何这么写。

写什么样的注释

  1. 公共的、全局的变量和常量:说明用在哪,提供给谁用。
  2. 函数,方法:说明函数功能是什么。
  3. 行注释:xx 产品在 x 年 x 月 x 日提出什么需求,作此修改。

注释不是用来删代码的!!!

代码不用了就完全删除,怕之后还有用就从 git 里找回来,若是一个函数 100 行,其中 50 行都被注释掉了,这种会很容易分散读者的注意力。

易修改

一值一用

不要把一个变量重复赋值使用。虽然类型同样,但这样作会让修改的人很是头疼,所谓牵一发而动全身。例如:

  • bad case:

result, err := a.Get()
result, err = b.Get()

  • good case:

resultA, err := a.Get()
resultB, err = b.Get()

少写参数

当你发现一个函数的功能须要传入七八个参数才能完成的时候,必定是函数干的事太多了,逻辑写太长了。须要适当拆分。

正确使用逻辑运算符

&&、||、!这些逻辑运算符是用来作逻辑判断的,不是用来控制执行流程的。

例如这样一段逻辑:

if (isA()) {
    doB()
}

不要写成 isA() && doB(),尽管结果是同样的。

适当化简

if ((condition1() && condition2()) || !condition1()) {
    return true
}
return false

取反后化简为:

if(condition1() && !condition2()) {
    return false
}
return true

下降圈复杂度

圈复杂度的定义:https://zh.wikipedia.org/wiki/循环复杂度

增长圈复杂度的关键词:

if、else、while、for、case、||、&& 等

圈复杂度的合格标准:

大部分标准在 10-20 之间。
这也是一个平均值,不是要求每个函数都在 20 如下。
个别超标,是能够接受的。

如何下降

1. 提炼函数
2. 抽象配置,使用 map
3. 合并返回值相同的函数

多写函数少写变量

实现一样的功能,并非代码越少越好。

由于代码越少每每意味着耦合度越高,修改扩展起来会更麻烦,就是爽了本身,给别人留坑。

可是,每一行代码都要有价值。

若是说逻辑节点之间须要一个东西来充当桥梁,变量就是独木桥,函数像隧道。
防杠精:有人说要考虑性能开销啊,多写一个函数比一个栈内的变量开销大啊,我以为业务代码不差这一星半点的,自行斟酌。

易测试

TDD

测试驱动开发:写一个函数以前先考虑写出来以后能不能测试,好很差测试。

实现方式

1

第一步:先写单元测试。没必要关心如何实现函数功能。

第二步:写目标函数,以恰好能经过单元测试的逻辑代码为目的。

第三步:重构函数,合理命名,优化结构,抽象设计。

如此循环,保证每次改动代码都能无缺地经过全部测试用例。

这样作的目的是让错误尽早的暴露出来,在 10 行代码中解决 bug 要比在 100 行代码中解决 bug 更加容易和快速。

理想与现实

看起来 TDD 的理论和可操做性还不错,但实际开发中,若是真的严格按照此理论去开发。对开发效率是一个比较大的影响。
并且一旦造成惯性思惟和盲从依赖,会下降对代码的灵感和熟练度。
测试用例跑过了就没问题了吗?不必定。由于测试用例也是人写的,隐藏的坑才最致命。
其实,当作到易阅读和易修改以后,易测试就是水到渠成的事情。

IoC 模式

将对象、接口、非固定值(如系统时间、随机数)等做为依赖注入,先构造条件,再执行函数。而不是由函数内部去构造。

全局变量单一写入方

当有两个以上的函数控制同一个全局变量的时候,会相互影响,即局部造成了一个状态机,使测试难度陡升。

封装外部依赖

有外部依赖的,尤为涉及 IO 通讯的,要单独封装,哪怕只有几行代码也要封装成一个独立的函数,不要把对外部依赖的调用混合在自身的逻辑代码中。

最后

阅读 → 修改 → 测试

这是一个递进的关系,环环相扣,而且前一步作好了都能有利于后一步的完善。

相关文章
相关标签/搜索