以Java为例,一个函数不应超过多少行呢?
大多数人都会说,函数不能太长,但也不能定死,要具体问题具体分析。再追问一下,有人会说,不超过200行,100行,50行,30行都有。另有人说,面向对象风格的可以短些,面向过程风格的可以长些。也有人说,一个方法不超过一屏幕就行(姑且不论显示器大小,字体大小和分辨率问题)。
先摘录一段Martin Fowler《重构》P110-P111 中的一段话:
引用
人们有时会问我,一个函数多长才合适?在我看来,长度不是问题,关键在于函数名称和函数本体之间的语义距离(semantic distance)。如果提炼动作(extracting)可以强化代码的清晰度,那就去做,就算函数名称比提炼出来的代码还长也无所谓。
说了直白点,函数名是干什么,函数体是怎么干。当2者不是那么能容易分辨出来时,就需要提炼出函数来,让代码更好懂。
既然马大师都说了,长度不是问题了吗,那干吗还要追究函数长度规范呢?
其实马大师讲的是对于懂得编程之道的人而言,不用关心长度,因为小函数是必然的结果。但在开发实践中,适当的函数长度限制值,可以给予我们警示,为什么这个方法写长了?哪里不对劲了?
对于这个问题我的回答是:Java函数不应超过15行。
为什么定这个数呢?
一方面,这个是基于多年开发实践的总结。15行其实已经是一个比较宽松的标准,开发人员稍加培训就可以实际贯彻,而符合这个标准的函数也能达到良好的可读性。
另一方面,这是基于对函数复杂度和代码行数之间关联关系的认识。
我采用一个简单的数学公式(注1):
引用
函数复杂度 >= 代码行数^2
以下为了简化,只以最低增长方式,平方数进行计算。
以一个函数的行数,从1行到20行为例,见下图:
当函数行数到达10行以上,函数复杂度就开始大幅度攀升。
当代码行数达到20行时(复杂度400),其复杂度已经是14行时(复杂度 196)的2倍。
因此我将代码规范定在15行。
下面我们运行此公式,来分析一下提炼小函数的好处。
例如,有1个100行的函数,分解为11(含自身)个10行的函数。
原复杂度= 100^2=10000。
分解后复杂度= 11×(10^2)= 1100。
分解后的复杂几乎是分解前的 1/9,几乎降低了1个数量级。这就解释了为什么长方法分解之后,可维护性大幅度提高。
再运用此公式,来观察那些现实中的长函数。
函数长度(Line) | 函数复杂度(Unit) |
10 | 100 |
15 | 225 |
20 | 400 |
30 | 900 |
50 | 2500 |
100 | 10000 |
500 | 250000 |
1000 | 1000000 |
也就无怪乎有同学表示维护1000行以上的函数,有种生不如死的感受。
注1:
该公式参考于 gerald m. weinberg 《质量·软件·管理(第1卷)——系统思维》