最近看了一份代码,我看的有点眩晕。想起来意大利面,意大利面个人还是蛮喜欢的。可是面对一份意面风格的代码,我就有点想吐了~
意面代码
意大利面,想必很多人都吃过。比较常见的长这样:
老外对糟糕的代码,有种有趣的叫法:Spaghetti code,意思就是意大利面条式代码。代码像面条一样混乱的堆成一团,很难理清头绪。
要确切地界定什么样的代码是意大利面条式代码,这好像无法准确的做一个定义。只能用栗子来描述,大体上什么样的代码可以称为意面式代码:
- 深度的条件嵌套风格,比如if条件嵌套很多层,switch 嵌套switch....
- 很多GOTO语句,当然这可能不常见,但是比如在Linux内核驱动的时候,你会看到很多栗子。但一般都是异常后在函数体内跳转。
- 混乱的return处理,比如多点return,这很容易导致bug。
- 滥用异常exception处理,甚至异常也嵌套
- 在面向对象编程中,设计混乱的类,或者非常长的类。有的代码,使用的是C++语言,可是写出来的代码却完全是面向过程式风格,当然这里想说的不是语言,而是编程思维,即便C语言也可以写出面向对象的赶脚。
- **圈复杂度(Cyclomatic Complexity)**很高的代码
- 函数写的非常长
- 全局变量满天飞,导致代码逻辑混乱,无法维护
- 条件语句内,过于复杂且条理不清的逻辑判断,一个if语句,整了一推语句与或非.....
- 混乱的多线程同步、通信处理
- .......
何为圈复杂度
圈复杂度(Cyclomatic Complexity),这个有可能很多朋友不知道。来简单描述一下什么是圈复杂度。
圈复杂度是由 Thomas McCabe 开发的一种用于确定程序的稳定性和复杂度的度量。也称为MCC指标。通过计算程序模块的线性独立路径的数量来度量。
圈复杂度,在有些认证要求严格的行业做需要计算,比如汽车电子、工业安全类产品、医疗器械等,在这些行业里,可能需要严格的单元测试,单元测试还需要严格的覆盖率指标要求。那么计算覆盖率,就离不开这个圈复杂度的计算。
对于严格计算圈复杂度,比较枯燥,这里就简单的分享一下如何计算的一般步骤:
- 将所有条件通路绘制成控制流图
- 流图中每多一个圈,MCC值为流图中圈数+1。
比如:
这样一个流图,其MCC值为5。
对于更为详细的计算圈复杂度的方法,可以看看IBM的文档:
https://www.ibm.com/docs/en/raa/6.1?topic=metrics-cyclomatic-complexity
显然,圈复杂度较低的程序更容易理解,修改的风险也更小。
意面指数
度量一份代码是否有点意面风格,看到一个有意思的指数分享一下。
全局变量数目行数
CC为圈复杂度,函数内读写全局变量的数目,函数的行数。
当然,这个公式是否严谨,我也不清楚。但是按这个计算,如果数字越高,显然代码的风格就越差。得分越高的代码,风格越差,越长的像意面。
得分高的代码,大体上有这些弊端
- 很难维护
- 很难懂
- 几乎无法做单元测试
- 非常容易潜藏bug,而且测试代价极高
- 这样的代码,喊别人给你review也是极度痛苦崩溃的
- .......
总结一下
意面虽好吃,但意面式代码还是要远离。本文分享一些个人的理解,尽量降低代码的圈复杂度,尽量少用全局变量,函数尽量别整太长。慢慢养成习惯,代码风格就会慢慢改善。