六种可定量分析的代码味道

       Sunny近年主要从事软件度量、设计模式、代码分析和优化、双向工程、代码自动重构等领域的研究工作,部分研究心得和研究成果也将通过本博客发布,我的研究宗旨是:软件工程研究应该为应用服务,不能脱离工程实践空谈理论大笑,因此,Sunny非常希望能够得到大家的意见和建议(特别是一线开发人员),包括代码自动生成、正向工程、逆向工程、代码缺陷和味道自动分析、代码自动重构、软件度量和质量、设计模式推荐、设计模式探测、设计模式标注、设计模式验证等领域。奋斗

       传统的代码味道(Bad Smells in Code)识别主要依赖开发人员的经验,这将导致代码味道识别效率低,且容易漏掉很多代码味道。因此,设计和开发自动化的代码味道识别工具已成为软件工程研究的一个分支。国内外学者已经陆续提出了一些代码味道自动识别方法,其中有一类方法是基于软件度量和统计量的,可以通过定量分析和计算来探测代码中是否存在这些代码味道,而且很多商业和开源的代码分析和优化工具也使用了其中一些方法,例如iPlasma、inFusion、PMD、Checkstyle、JDeodorant等中,本文将介绍六种可定量分析的代码味道,并介绍在一些常用的工具中它们的实现情况。【工具介绍参见:七款代码味道识别工具【简介】

 

(1) 重复代码 (Duplicated Code)

       重复代码是指相同的代码结构出现在多个地方。

       发现重复代码可以通过度量一个系统中重复代码行数所占百分比来实现。探测重复代码最大的问题在于存在多种不同的重复类型。完全相同的代码探测起来很容易,已存在不少技术。但是有一些重复代码存在重命名、使用别名等情况,导致探测技术需要对程序代码进行语义分析,例如,重命名局部变量等,这些探测方法需要更多的计算时间去检测各种可能的重命名。还有一些重复代码存在一些细微的修改或者中间混合了少量的其他代码,这导致探测的难度变大,可以引入相似度计算等技术。还有一种更复杂的重复代码,功能相同,结构不同,这种重复代码的检测需要深入分析程序代码的语义,寻找功能相同或相似的结构片段,无疑将导致探测过程更加复杂,探测难度加大,也需要更多的探测时间。

       在工具iPlasma和inFusion中,探测重复代码的策略是使用一组关于代码重复的度量指标,考虑到重复片段的长度和两段重复代码之间的距离。Checkstyle的实现非常简单,它简单统计程序源代码中的连续重复行数,设定的阈值为12,也就是说当有12行代码重复时则认为存在重复代码,重复代码的扫描可以跨方法甚至跨类。在PMD中,当一个代码片段重复至少一次且其中包含至少25个标记(tokens)时,则认为出现了重复代码坏味道。

 

(2) 依恋情节 (Feature Envy)

       依恋情节是指一个方法对另一个类的兴趣比对它所在类的兴趣还大。因为它与其他类之间具有更高的耦合度,故这个方法放在了错误的位置。

       依恋情节代码味道的探测可以通过度量一个方法对属于其他类中的方法(或数据)的耦合程度来实现。

       2006年,Lanza和Marinescu提出了如下方法来探测依恋情节:

       (1) 一个类中的方法直接使用了另一个类中的一些属性,可以通过访问外部数据个数(Access to Foreign Data, ATFD)来度量;

       (2) 一个类中的方法使用另一个类中的属性比使用自己本身的属性多得多,可以通过局部属性访问值(Locality of Attribute Accesses, LAA)来度量;

       (3) 一个类中所使用的“外部”属性属于少数几个其他类,可以通过使用外部数据提供者数(Foreign Data Providers, FDP)来度量。

       他们通过如下条件来探测依恋情节:

FDP ≤ FEW ATFD> FEW LAA < 1/3

       此处,Lanza和Marinescu设定FEW的值为5。

       iPlasma和inFusion采用了上述规则来识别依恋情节。在JDeodorant工具中,该问题转成了一个寻找Move Method(搬移方法)重构时机问题:它试图去寻找那些一旦搬移到另一个类中就很少使用外部(其他类中的)资源的方法。

六种可定量分析的代码味道_第1张图片

Object-Oriented Metrics in Practice

 

(3) 万能类/上帝类(God Class)

       万能类通常也认为是一种设计缺陷,它指的在系统中集多种功能于一身的类,它试图成为整个系统的中心。一个万能类承担了太多的职责,而只将很少的功能委托给其他不重要的类,并且万能类还需要从其他类那里获取数据。

       与依恋情节相同,2006年,Lanza和Marinescu还提出了一种通过计算三种度量指标来探测万能类的方法,这三种度量指标如下:

       (1) 加权方法计数(Weighted Method Count, WMC): 一个类中所有方法的统计复杂度的和。

       (2) 类内聚的紧密度(Tight Class Cohesion, TCC):通过访问相同的属性而直接发生联系的方法个数。

       (3) 访问外部数据个数(Access to Foreign Data, ATFD):对于一个给定的类,它所访问的外部类的个数,它可以直接访问这些外部类的属性,也可以通过访问器方法(Accessor Method)访问这些属性。

       在Lanza和Marinescu的方法中,当一个类满足如下条件时它就是一个万能类:

WMC ≥ VERY_HIGHATFD > FEW TCC < 1/3

       在此,Lanza和Marinescu设定VERY_HIGH的值为47,FEW的值为5,在他们的Object-Oriented Metrics in Practice一书中详细描述了这两个值是怎么获得的。

       iPlasma和inFusion采用了上述规则来识别万能类。JDeodorant通过其他类来判断一个类是否是万能类,并提出了Extract Class(提取类)的重构时机识别方法,这种探测方法与一个类本身的规模没有直接关系。

 

 (4) 过大类(Large Class)

       过大类是指一个类试图去做太多事情,这些类通常包含大量的成员变量和方法。

       类规模的度量方法有很多种,最传统的方式是度量源代码行数,如NLOC(Number of Lines Of Code)或者度量一个类中的属性数和方法数。像NLOC这种简单的度量指标可以表示一个类的规模,但是并不能判断一个类是否承担了多项职责,例如有些GUI类,对于一些复杂的界面,GUI类确实会比较大,但是这些代码基本上都是自动生成的,我们不应该认为它们存在过大类这一坏味道。

       PMD和Checkstyle等工具使用简单的NLOC作为过大类的探测策略。前者设定的源代码行数的阈值为1000,后者的阈值为2000。

 

(5) 过长方法 (Long Method)

       过长方法是指那些很长的方法。由于太长,导致难于理解、改变和扩展。也可以指一个方法承担了太多职责,使之成为一个类,甚至一个子系统的中心方法。

       度量过长方法看起来是一件很容易的事情,但是事实并非如此,依赖于一些简单的度量方式(例如源代码行数NLOC)可能会带来一些错误的结果,例如:有一些类的初始化方法(如构造函数)可能会比较长,由于它们的圈复杂度一般比较低,理解和修改较为容易,因此通常没有必要去重构那些长的初始化方法。Mäntylä建议使用圈复杂度(Cyclomatic Complexity)和Halstead度量(Halstead Measures)去识别过长方法,因为它们可以度量操作数和操作符的个数,并且提供了一些重要的有关方法复杂性的信息。根据Mäntylä的建议,最好的度量过长方法的度量指标应该是由源代码行数(NLOC)、圈复杂度和Halstead度量组合而成的一个多项式度量指标。

       Checkstyle和PMD等工具探测过长方法用的是最简单的源代码行数(NLOC),但是使用了不同的阈值,例如PMD的阈值为100而Checkstyle的阈值是150。JDeodorant使用了切片技术来判断一个类是否满足Extract Method(提取方法)重构时机。

 

(6) 过长参数列表 (Long Parameter List)

       过长参数列表是指一个方法的参数列表太长以致难以理解。

       过长参数列表的探测可以通过统计每个方法中参数的个数来实现。 探测过长参数列表的要点在于参数个数阈值的设定。例如,在PMD中默认的阈值数量为10,而在Checkstyle中为7。

 

【作者:刘伟  http://blog.csdn.net/lovelion】 

你可能感兴趣的:(重构,软件工程,代码质量,代码分析)