前言
很多包括自己在内的开发人员都会经常去借用(我们不用剽窃这个词了!呵呵)开源代码进行二次开发;或者在前辈的遗留代码下,继续修修补补。这种经历往往并不像看起来那么简单——有时看懂,进而修改别人的少许代码,都会觉得老虎天——无从下手,究其原因主要是代码晦涩,关系复杂,难以隔离影响等。
而这时我们或者抱怨前人代码写的愚蠢,垃圾;或者又会自惭自己编码水平太次。其实这种困境的起源除了自己笨以外,更多是因为代码的可维护性不够。
由于前不久和朋友齐永升注释《代码质量》一书时曾关注过代码的可维护性,而近期又在工作中不断遇到软件需求变更而带来的代码修改问题,所以这里就我自己对代码维护性进行一点总结,希望能引起大家注意,以便在以后开发中能养成好习惯。
软件维护性概念
所谓软件的可维护性其实说简单了就是软件代码的可被修改的容易程度。如前言所说,代码反复修改的情况不可避免,这种软件的不断演化过程——具体就是修正错误;适应新环境;满足新需求——虽然貌似将软件的功能变的越发强大,但是事实上这些改变总是或多或少的有悖于当初的设计初衷,因此势必慢慢的蚕食软件的基础架构和代码质量——造成的结果是让代码越来越难看懂,健壮性越来越脆弱,修改一个bug的代价越来越大。
鉴于这个矛盾,Martin Fowler提出的(refactor)代码重构主要就是从代码编写角度出发,提高代码的维护性,以便能更好适应软件演化。那么接下来的一个问题是:软件的可维护性有无标准的评测方法?学院派早都就此问题给出四个定义——:可分析性;可改变性;稳定性;易测性 。此刻先别去追究这个几个形而上学的术语 —— 后面我会就各点进一步展开,谈谈自己的看法。再次之前,我们先来看看定量评价可维护性的方法 (其实本文重点,不在于此,你完全可跳过以下两节)。
过程语言的可维护指数
首先来来谈谈面向过程语言的可维护性计算:这里有一个更貌似深奥的可维护性指数:
Maintainability Index (MI) = MAX(0,(171 - 5.2 * ln(Halstead Volume) - 0.23 * (Cyclomatic Complexity) - 16.2 * ln(Lines of Code))*100 / 171)
它看似一个对维护性定量分析的精确的数学公式。其实不然。这个公式无论是系数或者是运算项都是来自于经验规则(你千万别想着去推导它)
1. Halstead 是测量代码计算复杂度, 具体如果一个程序有N个操作数和运算符,n个不同的的操作数和运算符,那么halstead = N * Log2(n) ; 总之程序中的运算符和操作数越少越利于提高MI。
2. Cyclomatic Complexity 是代码的逻辑复杂度,程序的每个可能的执行分支(if, while,for 等)都为该指标贡献1个点。 该值的建议范围<10 ,最多不超过20。
3. Lines of code 是代码行数。
注意,需要说明的是每个模块,每个文件都可计算维护性指数,甚至每个函数都可计算,而程序的MI则是其所有的平均值。
如果你想测试一下你代码的MI,这样的工具开源的倒是有一些(http://www.chris-lott.org/resources/cmetrics/)。这里给大家推荐一个在Linux下的工具:pmccabe(apt-get install pmccabe即可安装)可用于计算Cyclomatic Complexity,line of code等 。可惜目前计算halstead的开源工具我还没有发现,不过好像vs 2008的新特性里支持代码复杂度计算,其中的各种指标还比较详细,建议大家可试用。
这个来自经验的MI的值越高越好,说明维护性越强。但是毕竟是经验规则,就如同我们有很多经验说如何买股票能挣钱,或者如何打仗能打胜,但是却绝对没有谁说一定这么作就能挣钱或者打胜仗。所以MI更合理的使用场合的是作为代码重构时的参考,便于发现那部分代码可能需要重点考虑。而且最好的评价方式是进行纵向比较,也就是说将你重构前的代码MI和重构后的比较,从而判断重构的积极意义。
面向对象的可维护指标
对于面向对象语言而言,可维护性的指标则更发散,更复杂一些。毕竟面向对象代码的基础单元是对象,而对象的封装,继承,多态这些特性无不影响代码的可维护性。下面介绍几个面向对象语言使用的通用指标。