章晓芳,朱 灿
(苏州大学 计算机科学与技术学院,江苏 苏州 215006)
代码坏味(code smell)通常代表不良的程序设计,会对程序理解与软件维护产生不良影响[1].
例如ClassDataShouldBePrivate这种坏味,暴露了类中的字段,违反了封装原则.为了减轻坏味对软件开发及演化的影响,近年来,研究者们对代码坏味进行了多方面的研究,包括代码坏味与开发者之间存在的关联[2]、代码坏味的演化[3-5]、坏味对代码理解与维护的影响[5,6]、坏味与源文件的变化倾向(change-proneness)及出错倾向(fault-proneness)之间的关系[1,7]等.
为帮助开发人员识别出存在于不同软件中的代码坏味,研究者们已经设计并实现了多种自动检测坏味的工具.这些工具基于多种分析技术,可以帮助识别不同语言的程序中存在的坏味[8-12].然而,即使这些工具可以相对准确并充分地检测出多种坏味,大多数的实证研究也仅仅局限在对软件已发布的单个版本进行探究.事实上,软件演化已经被认为是引起软件复杂度逐渐增加的重要因素之一[13,14].在演化过程中,软件开发人员对源文件的操作则是代码坏味在软件生命周期中演化的根本原因[15,16].基于此现状,充分了解软件源文件的操作与代码坏味变化之间的关联,将会有助于开发人员提升代码的设计、保证代码的质量.因此,本文系统地开展了一个详细的实证研究,以寻求存在于代码坏味与源文件操作之间的相互关联.具体来说,将源文件的改变细化为 3类最基本的操作:增加新文件、修改已有文件与移除无用文件.本文使用了广泛应用的坏味检测工具DÉCOR[17]来检测13种最常见的代码坏味,对8个Java项目共104个版本进行了有关源文件操作与代码坏味之间关联性的实证研究.
通过实证研究,本文得到如下结论:首先,随着版本的演化,含有坏味的文件在所有文件中的占比在不同的项目中呈现出不同的变化趋势,而在所有研究项目中,含有坏味的文件会发生改变的概率均较低;其次,含有代码坏味的文件在 3类具体操作中更倾向于被修改,而文件的添加、删除操作则与代码坏味并没有很大的关联;进一步地,如果文件中包含 ComplexClass和 LongParameterList这两种坏味,源文件将会有很大的被修改倾向,另外,AntiSingleton、Blob和ClassDataShouldBePrivate这几种坏味也会在一定程度上对文件的修改产生影响;最后,包含ComplexClass和LongParameterList这两种坏味的文件有较大的重叠,且包含这两种坏味的文件有较大的可能性包含其他类型的坏味.
有关代码坏味的研究一直是软件维护领域的热点之一.Fowler[18]首次提出“坏味”的概念,共描述了22种在软件发展与维护过程中存在设计缺陷的特殊代码结构.为了帮助开发者与研究者们识别代码坏味,很多研究人员致力于对坏味进行检测与等级划分[19-23],并对代码坏味的处理顺序给出了建议[24].在工具方面,Moha等人[17]提出的DÉCOR工具运用DETEX框架自动将坏味的定义转换为坏味检测,该工具已被广泛地运用在已有的代码坏味的研究中[25].
在坏味检测过程中,尽管已有多种坏味被定义,但其中有 13种坏味被认为对软件质量有着重要的影响[26],因此,本文使用DÉCOR工具对这13种坏味进行检测,表1给出了这13种坏味的具体描述及其简称.
Table 1 Description of code smells表1 代码坏味的描述
Table 1 Description of code smells (Continued)表1 代码坏味的描述(续)
为探究代码坏味与软件演化之间的关系,Chatzigeorgiou等人[27]研究了存在于面向对象程序中坏味的演化,并发现对于大多数的研究实例而言,坏味持久地存在于软件的发展与演化过程中.Olbrich等人[4]则具体针对GodClass和 ShotgunSurgery这两种坏味进行研究,研究表明,坏味的演化存在着不同的阶段、多样的改变频率与不同的改变规模.本文的研究不仅仅关注于坏味随着软件的演化呈现出的变化趋势,而是更深入地探究代码坏味是否与文件的添加、修改、删除这3类最基本的文件操作之间存在着某种程度上的相互关联.
针对代码坏味产生的原因,Tufano等人[28]进行了一个大规模的实证研究,来了解代码坏味何时会被引入以及为什么被引入.该研究通过统计坏味被引入之前源码文件被提交的次数,发现这些坏味通常在源码刚刚被添加到项目中时就被引入.另外,通过分析提交源码的目的、所属项目和开发者当时所处的状态,该研究试图更进一步地探究坏味被引入的原因.研究表明,在软件开发过程中,为增强软件现有的功能或增加新的功能时,开发人员往往更容易引入代码坏味.受到该研究的启发,本文将进一步探究是否所有被添加的源文件都和坏味存在着明显的相互关联.因此,对于每一个研究项目版本,将所有添加进这个版本的文件作为研究对象之一;同时,作为文件基本操作的修改与删除,也是本文的重点研究对象.
代码坏味的存在往往给软件的发展与演化带来一定的阻碍.Khomh等人[26]探究了坏味与代码变更及代码错误倾向之间的相互关联.该研究表明,在几乎所有被研究的项目版本中,包含坏味的源码比不含坏味的源码有着更易发生改变或是发生错误的倾向.同时,该研究也探讨了一些具体类型的坏味是否有更大的倾向发生错误或被修改.在此基础上,Palomba等人[29]开展了更大规模的实证研究,探究了代码坏味的分布及其对软件维护的影响.与上述研究不同的是,为了更好地了解代码坏味产生的影响,本文将发生改变的源码文件类型更细致地划分为新增、被修改与被移除的文件,从而分别探究这些最基本的文件操作与坏味之间的相互关联.
我们在前期的研究工作中[30,31]探讨了代码坏味与错误倾向之间的相互关联,在研究过程中发现,软件的演化实际上将细化为各个版本之间的文件变更,继而开始研究代码坏味与文件变更之间的关联.在此基础上,本文针对8个开源项目的共计104个版本开展了更大规模的实证研究,力求能进一步验证、扩充前期研究的结论,并深入探讨代码坏味与软件演化的关系,包括代码坏味与源文件操作的关系、坏味文件之间的相互关系等.
除了上述有关代码坏味对软件自身影响的相关研究,研究者们还关注到开发者们如何应对软件中存在的代码坏味[2],例如通过开发人员的具体行为来探寻坏味存在的生命周期等[15].
本文的实证研究致力于在文件级别上探究代码坏味与软件演化之间存在的关联,其中,我们将软件的演化具体为文件的添加、修改与移除操作.为了方便解释,我们首先给出一些相关定义如下.
定义1.项目P={Vi|i=1,…,n},Vi表示项目P中的第i个发布版本.
定义2.令存在于第i个版本中而不存在于第i-1版本中的文件为版本i的新增文件(added file),记为Ai;令在版本之间发生修改且同时存在于两版本之间的文件为被修改文件(modified file),记为Mi;令存在于第i个版本而不存在于第i+1个版本中的文件为被移除文件(removed file),记为Ri.令新增文件、被修改文件与被移除文件的合集为发生改变的文件,记为Ci.
定义 3.第i个版本中,令所有包含坏味的文件为坏味文件,记为Si;令所有不含坏味的文件为非坏味文件,记为Ni;令版本中所有的Java文件为Allfilesi.
定义4.第i个版本中,令含有坏味的文件在所有文件中的占比为坏味密度(smell density),计算方法如下.
定义5.第i个版本中,令含有坏味的文件发生变更的概率为坏味活跃度(smell activity),计算方法如下.
由定义4、定义5可见,活跃度的值反映了坏味文件在软件演化过程中发生改变的概率,通过计算坏味密度与坏味活跃度的值,将有助于分析坏味对软件演化的影响.
定义6.第i个版本中,令ChangeType指代新增、被修改和被移除这3种不同的文件变化类型,分别计算发生这3种不同类型改变的坏味文件在所有坏味文件中的占比以及发生改变的非坏味文件在所有非坏味文件中的占比,计算方法如下.
定义7.令包含某具体坏味的文件为smellm,不包含此类坏味的文件记为non-smellm.第i个版本中,通过获得包含该种坏味的文件与发生某种变化类型文件的交集,再除以包含此坏味文件的数量,可以得到包含此种坏味的文件发生该变化类型的占比.类似地,可以获得不包含此坏味的文件发生各类变化的占比,计算公式如下.
定义8.第i个版本中,令两种不同坏味文件的重叠率为包含坏味smellm,smelln的文件分别在包含这两种坏味的文件中占比,计算方法如下.
代码坏味随着软件项目的演化不断变化,一方面已有研究者表明,新增文件有很大的可能性会引入低质量甚至是错误的代码[28],因此随着软件项目规模的扩大,含有代码坏味的文件数量也会随之上升;而另一方面,代码重构也是提升软件项目可维护性的关键因素,在演化的过程中,开发者们往往会重构一些不好的程序设计,从而减少了含有代码坏味的文件数量[6].由此可见,代码坏味的密度可能会随着软件的演化呈现波动的状态.
坏味密度和坏味活跃度可以为开发人员提供重要的信息来做出开发过程中的关键决定,例如他们何时需要重构代码设计以及哪一部分代码需要被重构,又或者通过这些信息来判断软件项目是否达到了成熟阶段等.进一步地,本文分析坏味与文件改变之间的相互关联,有助于为软件项目的维护做出更好的重构计划.此外,不同的坏味可能存在于同一个的文件中,被不同坏味影响的文件是否存在较大的重叠,了解这样的重叠有助于减轻重构时的工作量,实现更为高效的软件维护.为实现这些研究目标,本文深入分析了代码坏味对软件演化的影响,其目的在于回答以下4个研究问题.
(1) 坏味密度与坏味活跃度随着软件项目的演化如何变化?这些变化具有什么样的特征?
(2) 当文件包含代码坏味时,更倾向于发生哪一种变化?文件的变化具体包括文件的新增、修改与删除,了解这些变化与坏味之间的关系是本文的重要目标.
(3) 具体是哪几种代码坏味与文件的变化有着更为显著的关联?在第2个问题的基础上,更进一步地去探究对文件变化产生相对较大影响的坏味类型.
(4) 被不同坏味影响的文件之间是否具有较大的重叠?同一个文件可能包含不同类型的坏味,在维护资源有限的情况下,是否可以仅考虑某几种类型的坏味加以重点维护.
本文主要对代码坏味与软件演化过程中文件不同类型的变化之间存在的关系进行探究,在实验对象选取及数据收集的过程中,需要综合考虑实验对象的多样性与数据的充分性两方面因素.所以,实验对象的选取遵循以下原则:实验对象需要具备不同的规模、属于不同领域,实现了不同的功能,分别由不同的团队进行研发,这样保证了研究对象的多样性;进一步地,这些实验对象需要具有足够多的版本、提交数量以及包含坏味文件的数量,因此优先考虑开发时间较长、发布版本较多的项目,以保证数据的充分性.文件发生改变的提交以及需要被检测的源码文件均可从 Github上获取,最终选取了 Che、Egit、Jmeter、Xerces2-j、Jgit、Tomcat、Nifi和Recommenders这8个开源的Java项目进行实证研究,这些实验对象的主要功能及简介如下.
(1) Che是一个公共云IDE平台,可以在公共云、私有云中运行,也可以在任何操作系统上安装.第1个版本于2016年2月16日发布,到目前为止共有115个版本,最新版本为6.9.0.
(2) Egit是一个用于处理Git存储库的Eclipse插件,已经发布了102个版本.第1个版本于2010年3月19日发布,最新版本为v5.0.2-r.
(3) Apache Jmeter是一个性能测试框架,可用于测试具有不同负载和各种配置下的静态、动态资源和Web动态应用程序.第1个版本于2011年 11月2日发布,到目前为止共有102个版本,最新版本为v4_0.
(4) Xerces2-j是一个高性能的完全兼容的XML解析器,迄今共发布了98个版本.第1个版本于1999年11月9日发布,最新版本是Xerces-J_2_12_0.
(5) Jgit是纯Java中Git版本控制系统的一个实现.共发布了107个版本,第1个版本于2010年3月19日发布,最新版本是v5.0.2-r.
(6) Apache Tomcat软件是Java Servlet、JavaServer Pages、Java Expression Language和Java WebSocket技术的开源实现.共发布了69个版本,第1个版本发布于2009年2月28日,最新版本为9_0_10.
(7) Apache NiFi是一个易于使用、功能强大且可靠的系统,用于处理和分发数据.第1个版本发布于2015年1月23日,迄今共63个版本,最新版本为rel/nifi-1.7.1.
(8) Recommenders通过一系列代码分析展示来实现代码推荐功能,共发布了60个版本.第1个版本发布于2011年2月16日,最新版本为v2.5.3.
由于这8个项目都拥有相对较多的发布版本,本文沿用Olbrich等人[4]研究工作中的版本选择策略,每5个发布版本选择 1个作为研究版本.此外,需要过滤掉无法使用项目管理工具自动编译和汇编的初始版本.有关实验对象的相关信息见表2,其中,|commit|表示第1个与最后一个版本之间所有提交的数量,|V|表示研究中选择出的版本数量,文件数量范围表示项目中拥有的所有Java文件的数量范围.
Table 2 Information of experiment subjects表2 实验对象信息
图1展示了本研究的实验流程:在确定好研究项目后,首先依据选择策略挑选出当前实验版本Vi;接着,根据定义2分别获取新增文件Ai与被移除文件Ri;随后,使用工具DIFF来获取版本之间(Vi与Vi+1之间)的被修改文件Mi;并使用DÉCOR区分出每一个实验版本中的坏味文件Si和非坏味文件Ni;最后,基于上述文件数据,计算并分析坏味文件与新增文件、被修改文件及被移除文件的关系,以了解每一种具体的代码坏味产生的影响.
Fig.1 Process of experiment图1 实验流程
对于每一个研究的版本i,根据公式(3)计算发生 3种不同类型改变的坏味文件在所有坏味文件中的占比,分别计为ASi,MSi,RSi.相应地,根据公式(4)计算发生 3种不同类型改变的非坏味文件在所有非坏味文件中的占比,表示为ANi,MNi,RNi.由此,对于 3种变化类型,可以分别比较坏味文件与非坏味文件发生这些变化的占比,从而分析出哪一种类型的变化与代码坏味有着显著的关联.
在了解了哪一种文件变化类型与代码坏味更为相关之后,我们进一步探究具体是哪一种或是哪几种坏味对这种变化类型的影响较为明显.根据公式(5)计算得到包含某坏味的文件发生特定类型的改变的占比,类似地,可以根据公式(6)获得不包含此坏味的文件发生各类变化的占比.
通过上述方法,可以得到代码坏味与 3种文件改变类型之间的相互关联,以及具体的坏味与这些改变之间的关系.此外,根据公式(7)和公式(8)计算坏味文件之间的重叠率,进一步地探究包含不同坏味的文件之间是否存在较多的重叠,这个探究旨在提供给开发人员更多的信息,以便更为高效地进行代码重构.
本文使用odds ratio(OR)来评估坏味文件与非坏味文件分别发生3类基本文件操作的倾向.OR值的计算方式如下所示.
其中,p代表了在一组样例中发生某种事件的比例,而q代表了另一组样例中同样事件发生的比例.本研究中,p是指坏味文件发生改变的比例,q则是非坏味文件发生改变的比例.换言之,p值对应了由公式(3)计算得来的AS,MS,RS,而q值则对应公式(4)中的AN,MN,RN.因此,OR>1表示当文件被坏味影响时有较大的可能性会发生这种类型的文件操作,而OR<1则代表了相反的含义.
另外,本文有两处使用了Wilcoxon符号秩检验:一是探究在AS,MS,RS数值之间是否存在较为显著的差异,二是探究被某一种坏味影响的文件发生改变的比例是否高于未被此种坏味影响的文件.Wilcoxon符号秩检验是一种非参数检验,因此不要求数据是正态分布的.如果检验得到的p-value值低于显著级,则拒绝原假设.
在此基础上,运用以下公式计算效应值(effect size),用于体现关联程度的强弱.
其中,Z表示所观察数据的方差,N表示被观察数据的总个数.根据Cohen’s效应值的分类,0.1≤r<0.3表示较小效应,0.3≤r<0.5表示中等效应,r≥0.5则表示具有较大效应.具体的,在本文中,若效应值低于 0.3,则表示该类型坏味与文件改变并无太大关联;若效应值高于0.5,则表示该类型坏味与文件改变具有较大的关联.
本节详细展示了8个项目的实验结果,并对实验结果做出相关讨论,以回答4个研究问题.
表3展示了研究的8个项目中坏味密度值的范围和均值.由表可见,代码坏味密度的值在除了Xerces2-j中较大外,在其他所有项目中都相对较小.
Table 3 Information of code smell density表3 代码坏味密度信息
图2为8个实验项目坏味密度变化的折线图,其中,横坐标代表项目的版本,纵坐标代表坏味密度值.
从坏味密度变化的趋势来看,Che、Egit、Jmeter、Tomcat、Recommenders这 5个项目的坏味密度呈现较为一致的下降趋势,而另外3个项目的坏味密度则呈现出不同幅度的振荡.进一步地,坏味密度下降的5个项目也呈现出多种下降特征:Che和 Egit这两个项目密度值的下降比较平缓,这是因为这两个项目的总文件数量随着项目的演化在平稳增长;Jmeter的总文件数量虽是同样呈现持续上升趋势,但是由于在第5个版本处含有LPL文件的数量忽然大幅下降,导致密度值在此处骤降;至于Tomcat,在第2个版本处总文件数量出现较大增长且包含AS的文件数量大幅减少导致密度骤降;而Recommenders则是由于在第4版本与第5版本处包含文件总数量发生了相对较大的增长,从而导致密度的陡然下降.另一方面,Xerces2-j、Jgit和Nifi这 3个坏味密度振荡的项目包含文件的总数量则是随着版本的演化持续波动,并非持续增长的.
基于坏味密度呈现出不同变化的趋势,单纯从密度值难以得到关于坏味演化的一致结论,因此进一步引入坏味活跃度来反映包含坏味的文件发生变化的占比来探究坏味对文件改变的影响.表4给出代码坏味活跃度的范围和均值.
由表4可见,绝大多数项目坏味活跃度的均值低于0.5,即在大多数情况下,只有一半不到的坏味文件将发生文件变更.其中,活跃度均值最高的 Recommenders项目是由于其前期版本中代码活跃度较高造成的,其后期版本中活跃度明显下降.结合坏味密度和活跃度发现:在坏味密度呈现下降的 5个项目中,坏味活跃度也呈现出下降的趋势.
Fig.2 Evolution of code smell density图2 代码坏味密度的演化
Table 4 Information of code smell activity表4 代码坏味活跃度信息
综上所述,坏味密度的变化呈现出了多种趋势,而坏味活跃度在大多项目中低于 0.5.由此,我们推测不同的项目处于不同的开发阶段,处于非成熟阶段的项目将引发诸多基础功能的改变,从而呈现不稳定的坏味密度与活跃度的演化.即使坏味密度持续下降,往往并非是因为开发人员对坏味存在的关注或是限制,而是因为总的文件数量的变化.因此,我们需要更深入地探究坏味对文件改变的具体影响.
图3展示了8个项目有关代码坏味与不同文件变化类型之间相互关联的盒图.盒图的横坐标是根据公式(3)和公式(4)计算得到的结果,分别记为 AS,MS,RS,AN,MN,RN,纵坐标表示坏味文件与非坏味文件中发生 3类不同具体操作的占比.情况相反.至于MS的数值范围,Che具有最小的数值范围,坏味文件发生修改的占比均小于0.25,而Jgit具有最高数值范围,最高占比大于0.9,其次是Xerces2-j.
Fig.3 Proportion of different change types in smelly and non-smelly files图3 坏味文件与非坏味文件发生不同变化的占比
从占比数值分布的角度来看,不同的研究项目具有不同的特征,但总体而言,我们有如下发现:在 AS,MS,RS几个数值中,MS具有最高中值,而几乎所有RS的中值最低;考虑非坏味文件的改变,AN,MN,RN中,MN也高于AN与RN.
为了进一步探究AS,MS,RS的数值分布之间是否具有显著差异,使用Wilcoxon符号秩检验进行评估.设置原假设如下.
(1)H01:AS值与MS值之间没有显著性差异;
(2)H02:MS值与RS值之间没有显著性差异;
(3)H03:AS值与RS值之间没有显著性差异.
表5展示了检验得到的p-value值,其中,A-B表示A,B两组数值对比得到的p-value值,如AS-MS表示H01的p-value值.若p-value值小于0.05,则拒绝原假设.从表5可知,对于H01和H02,所有AS-MS和MS-RS的p-value值均小于0.05,这意味着MS分别与AS,RS之间存在显著差异,因此拒绝假设H01与H02.对于H03,所有p-value值都大于0.05,不能拒绝假设H03,这表示AS与RS之间不存在显著差异.
Table 5 p-values for comparison of AS,MS and RS表5 AS,MS和RS的对比p-value值
进一步地,利用 OR值来评估坏味文件与非坏味文件发生改变的倾向.表6汇总了各个项目的 OR值,其中,A,M,R分别表示新增、被修改与被移除文件的 OR值.表中统计了每组OR>1和OR<1的数量,分别记为#(OR>1)和#(OR<1).
Table 6 Summary of change ORs表6 OR值汇总
由表6可知,对于新增文件的 OR 值,除了 Xerces2-j和 Nifi的所有其他项目,#(OR<1)高于#(OR>1).然而Xerces2-j 中#(OR>1)为 10,对应#(OR<1)为 8,Nifi 中#(OR>1)为 6,对应#(OR<1)为 5,这两个项目#(OR>1)与#(OR<1)十分接近.对于被修改文件的OR值,在所有项目中,#(OR>1)高于#(OR<1).对于被移除文件的OR值,除了 Recommenders项目中存在#(OR>1)等于#(OR<1)外,在其他所有项目中有:#(OR<1)高于#(OR>1).
OR值体现了代码坏味与文件的具体操作类型之间的关联,其中,被修改文件的OR值大于1,则代表了包含坏味的文件更倾向于被修改.根据上述实验结果与分析,我们可以得出以下结论:坏味文件更倾向于被修改,而代码坏味与文件的新增或是被移除没有很大的关联.
上述实证研究结果证实了Khomh等人[26]关于坏味文件更具有改变倾向、错误倾向的结论,进一步得出了坏味文件更倾向于被修改的结论,并从更大规模的项目实证再次证实了我们前期的研究成果[31].坏味文件更倾向于被修改的现象提醒开发人员,应在开发过程中重视代码坏味的检测和及时消除,以减少代码坏味对后续的开发与维护的不良影响.
基于第3.2节中代码坏味与文件修改更为相关的结果,本节进一步探究具体是哪一种或是哪几种坏味对文件修改产生较大的影响.因此,分别计算包含某一种坏味的文件与不包含这种坏味的文件发生修改的占比.
在对坏味进行检测的过程中发现,并非所有的13种坏味都存在于各个项目中.但能够检测到的坏味大多随着版本的演化一直存在于项目中,即这些坏味存在于每个项目的所有版本中.为了保证研究的可靠性,本文仅对存在于该项目所有版本中的代码坏味进行研究.这是由于要探究特定类型的坏味与文件修改之间的相互关联,若是其中某一版本中不存在受某种坏味影响的文件,不仅实验结果会产生误差,同时也因为不存在该坏味而缺失了研究的意义.
图4展示了被特定类型坏味影响的文件与未被该坏味影响的文件发生修改的占比.图4中使用了坏味名称缩写以便更加清晰地表示,如Bb表示Modified(Blob),non-Bb表示Modified(non-Blob).
Fig.4 Proportion of modified files in certain smell files and the files that not contain the smell图4 包含特定类型坏味的文件与不包含该坏味的文件被修改的占比
基于图4的信息,表7汇总了特定类型坏味与文件修改的关联信息,其中,“√”表示该种坏味在对应项目中表现出与文件修改有较大关联,“×”则表示关联度较小,“-”表示未检测到此种坏味.
Table 7 Correlations between code smell and file modification表7 坏味与文件修改的关联
从图4、表7中可以看出,所有项目中都包含被CC与LPL影响的文件,并且这些文件都有较大的被修改倾向;相反的,YC与文件修改不存在显著性关联.至于AS,Bb和CP这几种坏味,它们在不同的项目中对文件修改产生的影响则不尽相同:在 Che、Egit、Xerces2-j、Jgit、Tomcat、Recommenders中,包含 Bb的文件有较大可能性被修改,而在Nifi中,Bb并不呈现这样的特征;AS在Che中对文件修改的影响不大,而在Egit、Xerces2-j、Tomcat、Nifi、Recommenders中对文件的修改影响较大;Che、Jgit、Tomcat中,被 CP影响的文件发生修改的可能性不大,而另一些项目中被影响的文件有较大的可能性被修改.
进一步地,我们使用 Wilcoxon符号秩检验来探究包含特定类型坏味的文件是否与文件修改有着较强的相互关联.对于每一种特定类型的代码坏味,设置原假设H04:包含该种坏味的文件发生修改的占比与不包含该坏味的文件发生修改的占比不存在显著性差异.若检验得到的p-value值小于0.05,将否定原假设,并进一步计算效应值.表8展示了具体的p-value及效应值,当p-value值大于0.05时,对应数值加“*”表示.
Table 8 p-values and effect size表8 p-value值与效应值
如表8所示,有5种代码坏味需要引起我们的关注.具体来说,CC与LPL这2种坏味在所有8个项目中均有p-value值小于0.05,效应值大于0.5,充分说明这两种坏味会对文件的修改产生较大的影响.AS,Bb,CP则并非存在于所有的项目中,且对不同项目中文件修改的影响也不尽相同.例如,AS在 Tomcat和 Recommenders中的p-value值大于0.05,表示该坏味在这两个项目中与文件修改的关联度不大;而在另外的项目中则显示出较强的关联.类似地,Bb在Xerces2-j和Nifi这2个项目中,CP在Recommenders中的p-value值均大于0.05.
通过分析上述5种代码坏味与文件修改之间的关联,我们发现,CC,LPL与文件的修改之间存在较大的关联.另外,我们仍需多加关注 Bb,该坏味存在于除了 Jmeter以外的所有项目中,且在多个项目中还具有较大的效应值,对文件修改的影响也相对较大.AS与CP若是存在于项目中,也需要引起一定的关注.
基于第 3.3节中坏味与文件修改之间关联度的分析,本节计算受到 5种特定类型代码坏味(AS,Bb,CP,CC,LPL)影响的共同文件在各自坏味文件中的占比.为了便于观察与分析,表9仅列举各个项目中根据公式(7)和公式(8)计算得到的重叠率均值大于0.1的情况.
Table 9 Overlaps between specific smelly files表9 不同坏味文件的重叠率
从表9可知,受CC与LPL影响的文件在所有项目中的均值都高于0.1,其中,同时包含这两种坏味的文件在CC中的占比均值最低为0.165,最高为0.693;在LPL中的占比均值最低为0.135,最高为0.296.这表明CC与LPL这两种坏味存在着共生现象,往往同时存在于同一项目中,且这两种坏味文件有一定程度的重叠.这些现象与这两种坏味的定义相一致,这两种坏味分别代表了具有复杂圈复杂度、较多代码行数和长参数列表的代码片段.类似地,AS与CP在6个项目中存在着共生现象.值得注意的是,这两种坏味文件的重叠程度很大,在AS中的占比最高为0.924,在CP中的占比最高为0.956.这表明,项目中很大比例的文件同时受到AS与CP这两种坏味的影响.
进一步深入分析坏味文件的数量可以发现:虽然CC与LPL的重叠率整体上并不是很高,但在所有项目中,被 CC与 LPL同时影响的文件数量较多,例如,Jgit中同时包含这两种坏味的文件数量在各个版本中的均值为13,而其余坏味文件的重叠数量均值都低于3.对于AS与CP这两种坏味,除了Che中同时被这两种坏味影响的文件数量大于 65以外,其他项目中同时被这两种坏味影响的文件数量较少,最高仅为 19.在少量重叠文件而高重叠率的情况下,同时对AS与CP这两种坏味进行代码重构,消除坏味的影响将事半功倍.
此外,被CC与其他坏味共同影响的文件往往占据其他坏味文件较大的比例,例如AS_and_CC_in_AS在6个项目中重叠率高于0.1,而AS_and_CC_in_CC仅在2个项目中重叠率高于0.1.这说明CC坏味文件数量往往大于AS坏味文件数量.除CC之外,LPL也具有类似的特征.因此,坏味文件数量较高的CC与LPL应该在重构时优先考虑,以提高重构效率.
通过上述观察可知,在代码重构过程中,应综合考虑存在共生现象的CC与LPL、AS与CP坏味文件,力求共同消除.特别地,应该提高CC和LPL这两种坏味文件的维护优先级.此外,AS,Bb,CP这几种坏味也不可忽视.
存在于各个项目中不同的坏味密度与活跃度的特征,促使我们探究坏味与文件改变之间的相互关联.正如代码坏味的相关定义,坏味是指会影响代码结构与可理解性的不友好片段,我们的探究表明,坏味的存在会导致文件内容发生修改,而不会影响到整个项目的基本框架.我们推测,开发者们在开发过程中更注重实现功能需求而忽视坏味的存在,往往会给后续的开发与维护带来一定的困难.从另一个角度来看,如果坏味问题得到有效的解决,那么将极大地减少由坏味引起的文件修改的成本.
根据文件修改与具体坏味之间的关联与包含不同坏味文件之间的重叠率,我们的研究提供给开发者有关包含坏味文件维护的有效建议,如提升包含ComplexClass和LongParameterList这两种坏味文件的维护优先级、维护时可同时考虑消除AntiSingleton与ClassDataShouldBePrivate这两种坏味的影响等,以实现更为高效的代码维护.
和多数的实证研究一样,本文的工作也存在以下内部与外部因素对可靠性的影响.
(1) 内部影响:其中一个主要威胁是实验环境的准确性.首先,我们使用DÉCOR检测代码坏味,代码坏味的定义本身具有一定的主观性,这可能是一个对本研究可靠性的潜在威胁.但是作为一个被广泛应用的代码坏味自动检测工具,DÉCOR检测坏味的召回率为 100%,平均准确率为 60%,因此我们认为,由DÉCOR检测的坏味是可以用于该实证研究的.另外,我们对实验环境进行了仔细的检查与测试,以保证实验的有效性.
(2) 外部影响:我们仅对8个Java项目进行了实证研究.这些项目具有相对较大的规模并且应用于多个领域,并且我们共计探究了 104个项目版本,从这些版本中得到的数据足够支持我们的研究.我们相信,这样的实验规模足以保证该研究的有效性.然而,对更大的系统进行进一步验证,可以帮助推广我们的发现.此外,我们的实验是在文件级别进行的,将来我们可以将实验设置到类级别,以探索源代码和坏味之间更精确的相关性.
本文系统开展了关于代码坏味对软件演化影响的实证研究,分析了代码坏味与源文件变更之间的相关性.文件的变更细化为新增文件、修改文件和移除文件这3类具体操作,目的是探究这些不同的操作类型是否均与坏味相关.本文针对8个Java项目的104个版本,运用DÉCOR工具检测13种不同类型的坏味,实验结果表明.
(1) 代码坏味的密度和活跃度在不同的项目中呈现不同的特征,大部分项目的坏味密度随着软件的不断演化呈现下降趋势,且坏味活跃度通常不高.
(2) 与不含坏味的文件相比,含有坏味的文件更容易被修改.此外,代码坏味与文件的添加和移除没有明显的关联.
(3) ComplexClass和 LongParameterList这两类坏味与文件的修改更为相关;此外,如果项目中存在AntiSingleton、Blob和ClassDataShouldBePrivate这些坏味,仍然需要重视它们产生的影响.
(4) 软件维护的过程中可考虑AntiSingleton和ClassDataShouldBePrivate这两种坏味的特征同时进行重构;包含ComplexClass和LongParameterList这两种坏味的文件有较大的可能性包含其他坏味,且被这两种坏味影响的文件重叠率较高、重叠文件数量较多,应提高这两种坏味的重构优先级.
本文采用大规模的实证研究,深入分析了代码坏味对软件演化的影响,并为开发人员在软件维护过程中如何有效地重构代码给出了相关建议:开发人员应该更多地关注包含代码坏味的文件,尤其是包含 ComplexClass或LongParameterList的文件,避免引入这些坏味,将极大地降低文件被修改的可能性,从而降低整个软件生命周期中维护成本.在未来的工作中,将分析更多的项目,并在更为细化的级别上进行深入的实证研究.此外,拟引入人的因素,如熟悉度、中心性和所有权等,以讨论代码坏味产生的影响.