神奇数字式一个没有解释其意义的字面值,而且它出现在代码里多次。这样的值将会显示没有任何意图,它应该被替换为常量:常量的名字将会使代码更可读,在未来更加容易更新。
ç
让我们来看看一些代码,并且把它的神奇数字分离出来来试图理解它的情况。这是一个真实的数字的例子,来源于最近发布的一个关于120 kLOC的开源应用。
Value | Number of occurrences |
0.0 | 1 |
1.0E-08 | 1 |
0.05 | 1 |
0.33 | 1 |
1.0 | 10 |
1.7 | 4 |
2.0 | 1 |
2.3 | 1 |
3.2 | 1 |
4.0 | 1 |
4.199999 | 1 |
4.299999 | 1 |
5.0 | 1 |
5.2 | 2 |
7.3 | 1 |
8.0 | 1 |
365.25 | 3 |
3600.0 | 1 |
用眼快速地扫一下显示:
- 0.33 看上去像是 1/3, 有点取一个近似值。它可以被取代为用被除数3的除法。
- 365.25 看上去像一个一年的时间用天表示。另外一个可疑的地方时整数365和366在应用中使用了多次。
- 几个整数被表达为实数(0, 1, 2, 4, 5, 8, 3600)。
- 有一个小数点的浮点数看上去像版本号但也可能是其他的东西。我们能识别出PHP版本PHP version (5.0, 5.2), 或其他。
把字面值转变成常量
只使用一次的数字可以安全的被忽略。它们可能是用来做测试的目的或边缘用例,只有微小得重要性。只有至少2次出现的情况才值得创建一个常量。
另一个方面,使用多次的数字也可能显示不同代码之接有关系。当远程(如git版本控制)代码改变时,任何的更新突然变得困难起来。所有那些值必须被搜索和查阅。让我们来看看对于1.7的情况。
1.7 在代码里显示了4次。没有任何对这个值的意义的说明:最好的猜测是它可能是个版本号。Grep命令显示它在代码中出现了19次”1.7″:它真的有必要来使用审计工具来得到那些值。这是写实际的情况:
backend/external/misc/class.captcha.php#70: $fontsize = $this->height/1.7;
backend/external/misc/class.captcha.php#124: $fontsize = $this->height/1.7;
backend/external/misc/class.captcha.php#186: $fontsize = $this->height/1.7;
backend/external/pear.php.net/HTML/Common.php#97:
function apiVersion()
{
return 1.7;
} // end func apiVersion
有3次计算字体尺寸的出现平率。最后一个是被一个pear类返回的类的版本值。这里有两个相互冲突(不一样)的使用同一个数字的情况,通过常量来代替它们将会很有利。
推荐
- 查找出字面值,特别是那些用于算术的(像上面的计算字体大小的例子)并使用多次的。最主要的目标是常量化constantization (如果这个是个已存在的词的话).
- 神奇数字有可能是实数(很少),整数(很多),字符串(非常多)。按这样的顺序来处理它们来提高代码的可读性。
- 常量也许是类的常量(在类中使用)或接口(在多个接口相关的类)常量,或全局常量(为了测试目的)。记住,const关键字将会使常量更有效率,但是没有define灵活。
- 相同的字面值可以被定义为不同的常量:这是可能的而且很平常。
- ““全都是常量而且没有字面值”是一个坏主意。特别的,0, 1 和只使用了一次的字面值可以放着它们不管。
最后, 用常量来写下面的代码是非常漂亮的:
mkdir('path/to/new/dir', MODE_ALL_READ | MODE_ALL_EXEC)
而不要用:
mkdir('path/to/new/dir', 0770).