章曉芳,朱 燦
(蘇州大學(xué) 計(jì)算機(jī)科學(xué)與技術(shù)學(xué)院,江蘇 蘇州 215006)
代碼壞味(code smell)通常代表不良的程序設(shè)計(jì),會(huì)對(duì)程序理解與軟件維護(hù)產(chǎn)生不良影響[1].
例如ClassDataShouldBePrivate這種壞味,暴露了類中的字段,違反了封裝原則.為了減輕壞味對(duì)軟件開(kāi)發(fā)及演化的影響,近年來(lái),研究者們對(duì)代碼壞味進(jìn)行了多方面的研究,包括代碼壞味與開(kāi)發(fā)者之間存在的關(guān)聯(lián)[2]、代碼壞味的演化[3-5]、壞味對(duì)代碼理解與維護(hù)的影響[5,6]、壞味與源文件的變化傾向(change-proneness)及出錯(cuò)傾向(fault-proneness)之間的關(guān)系[1,7]等.
為幫助開(kāi)發(fā)人員識(shí)別出存在于不同軟件中的代碼壞味,研究者們已經(jīng)設(shè)計(jì)并實(shí)現(xiàn)了多種自動(dòng)檢測(cè)壞味的工具.這些工具基于多種分析技術(shù),可以幫助識(shí)別不同語(yǔ)言的程序中存在的壞味[8-12].然而,即使這些工具可以相對(duì)準(zhǔn)確并充分地檢測(cè)出多種壞味,大多數(shù)的實(shí)證研究也僅僅局限在對(duì)軟件已發(fā)布的單個(gè)版本進(jìn)行探究.事實(shí)上,軟件演化已經(jīng)被認(rèn)為是引起軟件復(fù)雜度逐漸增加的重要因素之一[13,14].在演化過(guò)程中,軟件開(kāi)發(fā)人員對(duì)源文件的操作則是代碼壞味在軟件生命周期中演化的根本原因[15,16].基于此現(xiàn)狀,充分了解軟件源文件的操作與代碼壞味變化之間的關(guān)聯(lián),將會(huì)有助于開(kāi)發(fā)人員提升代碼的設(shè)計(jì)、保證代碼的質(zhì)量.因此,本文系統(tǒng)地開(kāi)展了一個(gè)詳細(xì)的實(shí)證研究,以尋求存在于代碼壞味與源文件操作之間的相互關(guān)聯(lián).具體來(lái)說(shuō),將源文件的改變細(xì)化為 3類最基本的操作:增加新文件、修改已有文件與移除無(wú)用文件.本文使用了廣泛應(yīng)用的壞味檢測(cè)工具DéCOR[17]來(lái)檢測(cè)13種最常見(jiàn)的代碼壞味,對(duì)8個(gè)Java項(xiàng)目共104個(gè)版本進(jìn)行了有關(guān)源文件操作與代碼壞味之間關(guān)聯(lián)性的實(shí)證研究.
通過(guò)實(shí)證研究,本文得到如下結(jié)論:首先,隨著版本的演化,含有壞味的文件在所有文件中的占比在不同的項(xiàng)目中呈現(xiàn)出不同的變化趨勢(shì),而在所有研究項(xiàng)目中,含有壞味的文件會(huì)發(fā)生改變的概率均較低;其次,含有代碼壞味的文件在 3類具體操作中更傾向于被修改,而文件的添加、刪除操作則與代碼壞味并沒(méi)有很大的關(guān)聯(lián);進(jìn)一步地,如果文件中包含 ComplexClass和 LongParameterList這兩種壞味,源文件將會(huì)有很大的被修改傾向,另外,AntiSingleton、Blob和ClassDataShouldBePrivate這幾種壞味也會(huì)在一定程度上對(duì)文件的修改產(chǎn)生影響;最后,包含ComplexClass和LongParameterList這兩種壞味的文件有較大的重疊,且包含這兩種壞味的文件有較大的可能性包含其他類型的壞味.
有關(guān)代碼壞味的研究一直是軟件維護(hù)領(lǐng)域的熱點(diǎn)之一.Fowler[18]首次提出“壞味”的概念,共描述了22種在軟件發(fā)展與維護(hù)過(guò)程中存在設(shè)計(jì)缺陷的特殊代碼結(jié)構(gòu).為了幫助開(kāi)發(fā)者與研究者們識(shí)別代碼壞味,很多研究人員致力于對(duì)壞味進(jìn)行檢測(cè)與等級(jí)劃分[19-23],并對(duì)代碼壞味的處理順序給出了建議[24].在工具方面,Moha等人[17]提出的DéCOR工具運(yùn)用DETEX框架自動(dòng)將壞味的定義轉(zhuǎn)換為壞味檢測(cè),該工具已被廣泛地運(yùn)用在已有的代碼壞味的研究中[25].
在壞味檢測(cè)過(guò)程中,盡管已有多種壞味被定義,但其中有 13種壞味被認(rèn)為對(duì)軟件質(zhì)量有著重要的影響[26],因此,本文使用DéCOR工具對(duì)這13種壞味進(jìn)行檢測(cè),表1給出了這13種壞味的具體描述及其簡(jiǎn)稱.
Table 1 Description of code smells表1 代碼壞味的描述
Table 1 Description of code smells (Continued)表1 代碼壞味的描述(續(xù))
為探究代碼壞味與軟件演化之間的關(guān)系,Chatzigeorgiou等人[27]研究了存在于面向?qū)ο蟪绦蛑袎奈兜难莼?并發(fā)現(xiàn)對(duì)于大多數(shù)的研究實(shí)例而言,壞味持久地存在于軟件的發(fā)展與演化過(guò)程中.Olbrich等人[4]則具體針對(duì)GodClass和 ShotgunSurgery這兩種壞味進(jìn)行研究,研究表明,壞味的演化存在著不同的階段、多樣的改變頻率與不同的改變規(guī)模.本文的研究不僅僅關(guān)注于壞味隨著軟件的演化呈現(xiàn)出的變化趨勢(shì),而是更深入地探究代碼壞味是否與文件的添加、修改、刪除這3類最基本的文件操作之間存在著某種程度上的相互關(guān)聯(lián).
針對(duì)代碼壞味產(chǎn)生的原因,Tufano等人[28]進(jìn)行了一個(gè)大規(guī)模的實(shí)證研究,來(lái)了解代碼壞味何時(shí)會(huì)被引入以及為什么被引入.該研究通過(guò)統(tǒng)計(jì)壞味被引入之前源碼文件被提交的次數(shù),發(fā)現(xiàn)這些壞味通常在源碼剛剛被添加到項(xiàng)目中時(shí)就被引入.另外,通過(guò)分析提交源碼的目的、所屬項(xiàng)目和開(kāi)發(fā)者當(dāng)時(shí)所處的狀態(tài),該研究試圖更進(jìn)一步地探究壞味被引入的原因.研究表明,在軟件開(kāi)發(fā)過(guò)程中,為增強(qiáng)軟件現(xiàn)有的功能或增加新的功能時(shí),開(kāi)發(fā)人員往往更容易引入代碼壞味.受到該研究的啟發(fā),本文將進(jìn)一步探究是否所有被添加的源文件都和壞味存在著明顯的相互關(guān)聯(lián).因此,對(duì)于每一個(gè)研究項(xiàng)目版本,將所有添加進(jìn)這個(gè)版本的文件作為研究對(duì)象之一;同時(shí),作為文件基本操作的修改與刪除,也是本文的重點(diǎn)研究對(duì)象.
代碼壞味的存在往往給軟件的發(fā)展與演化帶來(lái)一定的阻礙.Khomh等人[26]探究了壞味與代碼變更及代碼錯(cuò)誤傾向之間的相互關(guān)聯(lián).該研究表明,在幾乎所有被研究的項(xiàng)目版本中,包含壞味的源碼比不含壞味的源碼有著更易發(fā)生改變或是發(fā)生錯(cuò)誤的傾向.同時(shí),該研究也探討了一些具體類型的壞味是否有更大的傾向發(fā)生錯(cuò)誤或被修改.在此基礎(chǔ)上,Palomba等人[29]開(kāi)展了更大規(guī)模的實(shí)證研究,探究了代碼壞味的分布及其對(duì)軟件維護(hù)的影響.與上述研究不同的是,為了更好地了解代碼壞味產(chǎn)生的影響,本文將發(fā)生改變的源碼文件類型更細(xì)致地劃分為新增、被修改與被移除的文件,從而分別探究這些最基本的文件操作與壞味之間的相互關(guān)聯(lián).
我們?cè)谇捌诘难芯抗ぷ髦衃30,31]探討了代碼壞味與錯(cuò)誤傾向之間的相互關(guān)聯(lián),在研究過(guò)程中發(fā)現(xiàn),軟件的演化實(shí)際上將細(xì)化為各個(gè)版本之間的文件變更,繼而開(kāi)始研究代碼壞味與文件變更之間的關(guān)聯(lián).在此基礎(chǔ)上,本文針對(duì)8個(gè)開(kāi)源項(xiàng)目的共計(jì)104個(gè)版本開(kāi)展了更大規(guī)模的實(shí)證研究,力求能進(jìn)一步驗(yàn)證、擴(kuò)充前期研究的結(jié)論,并深入探討代碼壞味與軟件演化的關(guān)系,包括代碼壞味與源文件操作的關(guān)系、壞味文件之間的相互關(guān)系等.
除了上述有關(guān)代碼壞味對(duì)軟件自身影響的相關(guān)研究,研究者們還關(guān)注到開(kāi)發(fā)者們?nèi)绾螒?yīng)對(duì)軟件中存在的代碼壞味[2],例如通過(guò)開(kāi)發(fā)人員的具體行為來(lái)探尋壞味存在的生命周期等[15].
本文的實(shí)證研究致力于在文件級(jí)別上探究代碼壞味與軟件演化之間存在的關(guān)聯(lián),其中,我們將軟件的演化具體為文件的添加、修改與移除操作.為了方便解釋,我們首先給出一些相關(guān)定義如下.
定義1.項(xiàng)目P={Vi|i=1,…,n},Vi表示項(xiàng)目P中的第i個(gè)發(fā)布版本.
定義2.令存在于第i個(gè)版本中而不存在于第i-1版本中的文件為版本i的新增文件(added file),記為Ai;令在版本之間發(fā)生修改且同時(shí)存在于兩版本之間的文件為被修改文件(modified file),記為Mi;令存在于第i個(gè)版本而不存在于第i+1個(gè)版本中的文件為被移除文件(removed file),記為Ri.令新增文件、被修改文件與被移除文件的合集為發(fā)生改變的文件,記為Ci.
定義 3.第i個(gè)版本中,令所有包含壞味的文件為壞味文件,記為Si;令所有不含壞味的文件為非壞味文件,記為Ni;令版本中所有的Java文件為Allfilesi.
定義4.第i個(gè)版本中,令含有壞味的文件在所有文件中的占比為壞味密度(smell density),計(jì)算方法如下.
定義5.第i個(gè)版本中,令含有壞味的文件發(fā)生變更的概率為壞味活躍度(smell activity),計(jì)算方法如下.
由定義4、定義5可見(jiàn),活躍度的值反映了壞味文件在軟件演化過(guò)程中發(fā)生改變的概率,通過(guò)計(jì)算壞味密度與壞味活躍度的值,將有助于分析壞味對(duì)軟件演化的影響.
定義6.第i個(gè)版本中,令ChangeType指代新增、被修改和被移除這3種不同的文件變化類型,分別計(jì)算發(fā)生這3種不同類型改變的壞味文件在所有壞味文件中的占比以及發(fā)生改變的非壞味文件在所有非壞味文件中的占比,計(jì)算方法如下.
定義7.令包含某具體壞味的文件為smellm,不包含此類壞味的文件記為non-smellm.第i個(gè)版本中,通過(guò)獲得包含該種壞味的文件與發(fā)生某種變化類型文件的交集,再除以包含此壞味文件的數(shù)量,可以得到包含此種壞味的文件發(fā)生該變化類型的占比.類似地,可以獲得不包含此壞味的文件發(fā)生各類變化的占比,計(jì)算公式如下.
定義8.第i個(gè)版本中,令兩種不同壞味文件的重疊率為包含壞味smellm,smelln的文件分別在包含這兩種壞味的文件中占比,計(jì)算方法如下.
代碼壞味隨著軟件項(xiàng)目的演化不斷變化,一方面已有研究者表明,新增文件有很大的可能性會(huì)引入低質(zhì)量甚至是錯(cuò)誤的代碼[28],因此隨著軟件項(xiàng)目規(guī)模的擴(kuò)大,含有代碼壞味的文件數(shù)量也會(huì)隨之上升;而另一方面,代碼重構(gòu)也是提升軟件項(xiàng)目可維護(hù)性的關(guān)鍵因素,在演化的過(guò)程中,開(kāi)發(fā)者們往往會(huì)重構(gòu)一些不好的程序設(shè)計(jì),從而減少了含有代碼壞味的文件數(shù)量[6].由此可見(jiàn),代碼壞味的密度可能會(huì)隨著軟件的演化呈現(xiàn)波動(dòng)的狀態(tài).
壞味密度和壞味活躍度可以為開(kāi)發(fā)人員提供重要的信息來(lái)做出開(kāi)發(fā)過(guò)程中的關(guān)鍵決定,例如他們何時(shí)需要重構(gòu)代碼設(shè)計(jì)以及哪一部分代碼需要被重構(gòu),又或者通過(guò)這些信息來(lái)判斷軟件項(xiàng)目是否達(dá)到了成熟階段等.進(jìn)一步地,本文分析壞味與文件改變之間的相互關(guān)聯(lián),有助于為軟件項(xiàng)目的維護(hù)做出更好的重構(gòu)計(jì)劃.此外,不同的壞味可能存在于同一個(gè)的文件中,被不同壞味影響的文件是否存在較大的重疊,了解這樣的重疊有助于減輕重構(gòu)時(shí)的工作量,實(shí)現(xiàn)更為高效的軟件維護(hù).為實(shí)現(xiàn)這些研究目標(biāo),本文深入分析了代碼壞味對(duì)軟件演化的影響,其目的在于回答以下4個(gè)研究問(wèn)題.
(1) 壞味密度與壞味活躍度隨著軟件項(xiàng)目的演化如何變化?這些變化具有什么樣的特征?
(2) 當(dāng)文件包含代碼壞味時(shí),更傾向于發(fā)生哪一種變化?文件的變化具體包括文件的新增、修改與刪除,了解這些變化與壞味之間的關(guān)系是本文的重要目標(biāo).
(3) 具體是哪幾種代碼壞味與文件的變化有著更為顯著的關(guān)聯(lián)?在第2個(gè)問(wèn)題的基礎(chǔ)上,更進(jìn)一步地去探究對(duì)文件變化產(chǎn)生相對(duì)較大影響的壞味類型.
(4) 被不同壞味影響的文件之間是否具有較大的重疊?同一個(gè)文件可能包含不同類型的壞味,在維護(hù)資源有限的情況下,是否可以僅考慮某幾種類型的壞味加以重點(diǎn)維護(hù).
本文主要對(duì)代碼壞味與軟件演化過(guò)程中文件不同類型的變化之間存在的關(guān)系進(jìn)行探究,在實(shí)驗(yàn)對(duì)象選取及數(shù)據(jù)收集的過(guò)程中,需要綜合考慮實(shí)驗(yàn)對(duì)象的多樣性與數(shù)據(jù)的充分性兩方面因素.所以,實(shí)驗(yàn)對(duì)象的選取遵循以下原則:實(shí)驗(yàn)對(duì)象需要具備不同的規(guī)模、屬于不同領(lǐng)域,實(shí)現(xiàn)了不同的功能,分別由不同的團(tuán)隊(duì)進(jìn)行研發(fā),這樣保證了研究對(duì)象的多樣性;進(jìn)一步地,這些實(shí)驗(yàn)對(duì)象需要具有足夠多的版本、提交數(shù)量以及包含壞味文件的數(shù)量,因此優(yōu)先考慮開(kāi)發(fā)時(shí)間較長(zhǎng)、發(fā)布版本較多的項(xiàng)目,以保證數(shù)據(jù)的充分性.文件發(fā)生改變的提交以及需要被檢測(cè)的源碼文件均可從 Github上獲取,最終選取了 Che、Egit、Jmeter、Xerces2-j、Jgit、Tomcat、Nifi和Recommenders這8個(gè)開(kāi)源的Java項(xiàng)目進(jìn)行實(shí)證研究,這些實(shí)驗(yàn)對(duì)象的主要功能及簡(jiǎn)介如下.
(1) Che是一個(gè)公共云IDE平臺(tái),可以在公共云、私有云中運(yùn)行,也可以在任何操作系統(tǒng)上安裝.第1個(gè)版本于2016年2月16日發(fā)布,到目前為止共有115個(gè)版本,最新版本為6.9.0.
(2) Egit是一個(gè)用于處理Git存儲(chǔ)庫(kù)的Eclipse插件,已經(jīng)發(fā)布了102個(gè)版本.第1個(gè)版本于2010年3月19日發(fā)布,最新版本為v5.0.2-r.
(3) Apache Jmeter是一個(gè)性能測(cè)試框架,可用于測(cè)試具有不同負(fù)載和各種配置下的靜態(tài)、動(dòng)態(tài)資源和Web動(dòng)態(tài)應(yīng)用程序.第1個(gè)版本于2011年 11月2日發(fā)布,到目前為止共有102個(gè)版本,最新版本為v4_0.
(4) Xerces2-j是一個(gè)高性能的完全兼容的XML解析器,迄今共發(fā)布了98個(gè)版本.第1個(gè)版本于1999年11月9日發(fā)布,最新版本是Xerces-J_2_12_0.
(5) Jgit是純Java中Git版本控制系統(tǒng)的一個(gè)實(shí)現(xiàn).共發(fā)布了107個(gè)版本,第1個(gè)版本于2010年3月19日發(fā)布,最新版本是v5.0.2-r.
(6) Apache Tomcat軟件是Java Servlet、JavaServer Pages、Java Expression Language和Java WebSocket技術(shù)的開(kāi)源實(shí)現(xiàn).共發(fā)布了69個(gè)版本,第1個(gè)版本發(fā)布于2009年2月28日,最新版本為9_0_10.
(7) Apache NiFi是一個(gè)易于使用、功能強(qiáng)大且可靠的系統(tǒng),用于處理和分發(fā)數(shù)據(jù).第1個(gè)版本發(fā)布于2015年1月23日,迄今共63個(gè)版本,最新版本為rel/nifi-1.7.1.
(8) Recommenders通過(guò)一系列代碼分析展示來(lái)實(shí)現(xiàn)代碼推薦功能,共發(fā)布了60個(gè)版本.第1個(gè)版本發(fā)布于2011年2月16日,最新版本為v2.5.3.
由于這8個(gè)項(xiàng)目都擁有相對(duì)較多的發(fā)布版本,本文沿用Olbrich等人[4]研究工作中的版本選擇策略,每5個(gè)發(fā)布版本選擇 1個(gè)作為研究版本.此外,需要過(guò)濾掉無(wú)法使用項(xiàng)目管理工具自動(dòng)編譯和匯編的初始版本.有關(guān)實(shí)驗(yàn)對(duì)象的相關(guān)信息見(jiàn)表2,其中,|commit|表示第1個(gè)與最后一個(gè)版本之間所有提交的數(shù)量,|V|表示研究中選擇出的版本數(shù)量,文件數(shù)量范圍表示項(xiàng)目中擁有的所有Java文件的數(shù)量范圍.
Table 2 Information of experiment subjects表2 實(shí)驗(yàn)對(duì)象信息
圖1展示了本研究的實(shí)驗(yàn)流程:在確定好研究項(xiàng)目后,首先依據(jù)選擇策略挑選出當(dāng)前實(shí)驗(yàn)版本Vi;接著,根據(jù)定義2分別獲取新增文件Ai與被移除文件Ri;隨后,使用工具DIFF來(lái)獲取版本之間(Vi與Vi+1之間)的被修改文件Mi;并使用DéCOR區(qū)分出每一個(gè)實(shí)驗(yàn)版本中的壞味文件Si和非壞味文件Ni;最后,基于上述文件數(shù)據(jù),計(jì)算并分析壞味文件與新增文件、被修改文件及被移除文件的關(guān)系,以了解每一種具體的代碼壞味產(chǎn)生的影響.
Fig.1 Process of experiment圖1 實(shí)驗(yàn)流程
對(duì)于每一個(gè)研究的版本i,根據(jù)公式(3)計(jì)算發(fā)生 3種不同類型改變的壞味文件在所有壞味文件中的占比,分別計(jì)為ASi,MSi,RSi.相應(yīng)地,根據(jù)公式(4)計(jì)算發(fā)生 3種不同類型改變的非壞味文件在所有非壞味文件中的占比,表示為ANi,MNi,RNi.由此,對(duì)于 3種變化類型,可以分別比較壞味文件與非壞味文件發(fā)生這些變化的占比,從而分析出哪一種類型的變化與代碼壞味有著顯著的關(guān)聯(lián).
在了解了哪一種文件變化類型與代碼壞味更為相關(guān)之后,我們進(jìn)一步探究具體是哪一種或是哪幾種壞味對(duì)這種變化類型的影響較為明顯.根據(jù)公式(5)計(jì)算得到包含某壞味的文件發(fā)生特定類型的改變的占比,類似地,可以根據(jù)公式(6)獲得不包含此壞味的文件發(fā)生各類變化的占比.
通過(guò)上述方法,可以得到代碼壞味與 3種文件改變類型之間的相互關(guān)聯(lián),以及具體的壞味與這些改變之間的關(guān)系.此外,根據(jù)公式(7)和公式(8)計(jì)算壞味文件之間的重疊率,進(jìn)一步地探究包含不同壞味的文件之間是否存在較多的重疊,這個(gè)探究旨在提供給開(kāi)發(fā)人員更多的信息,以便更為高效地進(jìn)行代碼重構(gòu).
本文使用odds ratio(OR)來(lái)評(píng)估壞味文件與非壞味文件分別發(fā)生3類基本文件操作的傾向.OR值的計(jì)算方式如下所示.
其中,p代表了在一組樣例中發(fā)生某種事件的比例,而q代表了另一組樣例中同樣事件發(fā)生的比例.本研究中,p是指壞味文件發(fā)生改變的比例,q則是非壞味文件發(fā)生改變的比例.換言之,p值對(duì)應(yīng)了由公式(3)計(jì)算得來(lái)的AS,MS,RS,而q值則對(duì)應(yīng)公式(4)中的AN,MN,RN.因此,OR>1表示當(dāng)文件被壞味影響時(shí)有較大的可能性會(huì)發(fā)生這種類型的文件操作,而OR<1則代表了相反的含義.
另外,本文有兩處使用了Wilcoxon符號(hào)秩檢驗(yàn):一是探究在AS,MS,RS數(shù)值之間是否存在較為顯著的差異,二是探究被某一種壞味影響的文件發(fā)生改變的比例是否高于未被此種壞味影響的文件.Wilcoxon符號(hào)秩檢驗(yàn)是一種非參數(shù)檢驗(yàn),因此不要求數(shù)據(jù)是正態(tài)分布的.如果檢驗(yàn)得到的p-value值低于顯著級(jí),則拒絕原假設(shè).
在此基礎(chǔ)上,運(yùn)用以下公式計(jì)算效應(yīng)值(effect size),用于體現(xiàn)關(guān)聯(lián)程度的強(qiáng)弱.
其中,Z表示所觀察數(shù)據(jù)的方差,N表示被觀察數(shù)據(jù)的總個(gè)數(shù).根據(jù)Cohen’s效應(yīng)值的分類,0.1≤r<0.3表示較小效應(yīng),0.3≤r<0.5表示中等效應(yīng),r≥0.5則表示具有較大效應(yīng).具體的,在本文中,若效應(yīng)值低于 0.3,則表示該類型壞味與文件改變并無(wú)太大關(guān)聯(lián);若效應(yīng)值高于0.5,則表示該類型壞味與文件改變具有較大的關(guān)聯(lián).
本節(jié)詳細(xì)展示了8個(gè)項(xiàng)目的實(shí)驗(yàn)結(jié)果,并對(duì)實(shí)驗(yàn)結(jié)果做出相關(guān)討論,以回答4個(gè)研究問(wèn)題.
表3展示了研究的8個(gè)項(xiàng)目中壞味密度值的范圍和均值.由表可見(jiàn),代碼壞味密度的值在除了Xerces2-j中較大外,在其他所有項(xiàng)目中都相對(duì)較小.
Table 3 Information of code smell density表3 代碼壞味密度信息
圖2為8個(gè)實(shí)驗(yàn)項(xiàng)目壞味密度變化的折線圖,其中,橫坐標(biāo)代表項(xiàng)目的版本,縱坐標(biāo)代表壞味密度值.
從壞味密度變化的趨勢(shì)來(lái)看,Che、Egit、Jmeter、Tomcat、Recommenders這 5個(gè)項(xiàng)目的壞味密度呈現(xiàn)較為一致的下降趨勢(shì),而另外3個(gè)項(xiàng)目的壞味密度則呈現(xiàn)出不同幅度的振蕩.進(jìn)一步地,壞味密度下降的5個(gè)項(xiàng)目也呈現(xiàn)出多種下降特征:Che和 Egit這兩個(gè)項(xiàng)目密度值的下降比較平緩,這是因?yàn)檫@兩個(gè)項(xiàng)目的總文件數(shù)量隨著項(xiàng)目的演化在平穩(wěn)增長(zhǎng);Jmeter的總文件數(shù)量雖是同樣呈現(xiàn)持續(xù)上升趨勢(shì),但是由于在第5個(gè)版本處含有LPL文件的數(shù)量忽然大幅下降,導(dǎo)致密度值在此處驟降;至于Tomcat,在第2個(gè)版本處總文件數(shù)量出現(xiàn)較大增長(zhǎng)且包含AS的文件數(shù)量大幅減少導(dǎo)致密度驟降;而Recommenders則是由于在第4版本與第5版本處包含文件總數(shù)量發(fā)生了相對(duì)較大的增長(zhǎng),從而導(dǎo)致密度的陡然下降.另一方面,Xerces2-j、Jgit和Nifi這 3個(gè)壞味密度振蕩的項(xiàng)目包含文件的總數(shù)量則是隨著版本的演化持續(xù)波動(dòng),并非持續(xù)增長(zhǎng)的.
基于壞味密度呈現(xiàn)出不同變化的趨勢(shì),單純從密度值難以得到關(guān)于壞味演化的一致結(jié)論,因此進(jìn)一步引入壞味活躍度來(lái)反映包含壞味的文件發(fā)生變化的占比來(lái)探究壞味對(duì)文件改變的影響.表4給出代碼壞味活躍度的范圍和均值.
由表4可見(jiàn),絕大多數(shù)項(xiàng)目壞味活躍度的均值低于0.5,即在大多數(shù)情況下,只有一半不到的壞味文件將發(fā)生文件變更.其中,活躍度均值最高的 Recommenders項(xiàng)目是由于其前期版本中代碼活躍度較高造成的,其后期版本中活躍度明顯下降.結(jié)合壞味密度和活躍度發(fā)現(xiàn):在壞味密度呈現(xiàn)下降的 5個(gè)項(xiàng)目中,壞味活躍度也呈現(xiàn)出下降的趨勢(shì).
Fig.2 Evolution of code smell density圖2 代碼壞味密度的演化
Table 4 Information of code smell activity表4 代碼壞味活躍度信息
綜上所述,壞味密度的變化呈現(xiàn)出了多種趨勢(shì),而壞味活躍度在大多項(xiàng)目中低于 0.5.由此,我們推測(cè)不同的項(xiàng)目處于不同的開(kāi)發(fā)階段,處于非成熟階段的項(xiàng)目將引發(fā)諸多基礎(chǔ)功能的改變,從而呈現(xiàn)不穩(wěn)定的壞味密度與活躍度的演化.即使壞味密度持續(xù)下降,往往并非是因?yàn)殚_(kāi)發(fā)人員對(duì)壞味存在的關(guān)注或是限制,而是因?yàn)榭偟奈募?shù)量的變化.因此,我們需要更深入地探究壞味對(duì)文件改變的具體影響.
圖3展示了8個(gè)項(xiàng)目有關(guān)代碼壞味與不同文件變化類型之間相互關(guān)聯(lián)的盒圖.盒圖的橫坐標(biāo)是根據(jù)公式(3)和公式(4)計(jì)算得到的結(jié)果,分別記為 AS,MS,RS,AN,MN,RN,縱坐標(biāo)表示壞味文件與非壞味文件中發(fā)生 3類不同具體操作的占比.情況相反.至于MS的數(shù)值范圍,Che具有最小的數(shù)值范圍,壞味文件發(fā)生修改的占比均小于0.25,而Jgit具有最高數(shù)值范圍,最高占比大于0.9,其次是Xerces2-j.
Fig.3 Proportion of different change types in smelly and non-smelly files圖3 壞味文件與非壞味文件發(fā)生不同變化的占比
從占比數(shù)值分布的角度來(lái)看,不同的研究項(xiàng)目具有不同的特征,但總體而言,我們有如下發(fā)現(xiàn):在 AS,MS,RS幾個(gè)數(shù)值中,MS具有最高中值,而幾乎所有RS的中值最低;考慮非壞味文件的改變,AN,MN,RN中,MN也高于AN與RN.
為了進(jìn)一步探究AS,MS,RS的數(shù)值分布之間是否具有顯著差異,使用Wilcoxon符號(hào)秩檢驗(yàn)進(jìn)行評(píng)估.設(shè)置原假設(shè)如下.
(1)H01:AS值與MS值之間沒(méi)有顯著性差異;
(2)H02:MS值與RS值之間沒(méi)有顯著性差異;
(3)H03:AS值與RS值之間沒(méi)有顯著性差異.
表5展示了檢驗(yàn)得到的p-value值,其中,A-B表示A,B兩組數(shù)值對(duì)比得到的p-value值,如AS-MS表示H01的p-value值.若p-value值小于0.05,則拒絕原假設(shè).從表5可知,對(duì)于H01和H02,所有AS-MS和MS-RS的p-value值均小于0.05,這意味著MS分別與AS,RS之間存在顯著差異,因此拒絕假設(shè)H01與H02.對(duì)于H03,所有p-value值都大于0.05,不能拒絕假設(shè)H03,這表示AS與RS之間不存在顯著差異.
Table 5 p-values for comparison of AS,MS and RS表5 AS,MS和RS的對(duì)比p-value值
進(jìn)一步地,利用 OR值來(lái)評(píng)估壞味文件與非壞味文件發(fā)生改變的傾向.表6匯總了各個(gè)項(xiàng)目的 OR值,其中,A,M,R分別表示新增、被修改與被移除文件的 OR值.表中統(tǒng)計(jì)了每組OR>1和OR<1的數(shù)量,分別記為#(OR>1)和#(OR<1).
Table 6 Summary of change ORs表6 OR值匯總
由表6可知,對(duì)于新增文件的 OR 值,除了 Xerces2-j和 Nifi的所有其他項(xiàng)目,#(OR<1)高于#(OR>1).然而Xerces2-j 中#(OR>1)為 10,對(duì)應(yīng)#(OR<1)為 8,Nifi 中#(OR>1)為 6,對(duì)應(yīng)#(OR<1)為 5,這兩個(gè)項(xiàng)目#(OR>1)與#(OR<1)十分接近.對(duì)于被修改文件的OR值,在所有項(xiàng)目中,#(OR>1)高于#(OR<1).對(duì)于被移除文件的OR值,除了 Recommenders項(xiàng)目中存在#(OR>1)等于#(OR<1)外,在其他所有項(xiàng)目中有:#(OR<1)高于#(OR>1).
OR值體現(xiàn)了代碼壞味與文件的具體操作類型之間的關(guān)聯(lián),其中,被修改文件的OR值大于1,則代表了包含壞味的文件更傾向于被修改.根據(jù)上述實(shí)驗(yàn)結(jié)果與分析,我們可以得出以下結(jié)論:壞味文件更傾向于被修改,而代碼壞味與文件的新增或是被移除沒(méi)有很大的關(guān)聯(lián).
上述實(shí)證研究結(jié)果證實(shí)了Khomh等人[26]關(guān)于壞味文件更具有改變傾向、錯(cuò)誤傾向的結(jié)論,進(jìn)一步得出了壞味文件更傾向于被修改的結(jié)論,并從更大規(guī)模的項(xiàng)目實(shí)證再次證實(shí)了我們前期的研究成果[31].壞味文件更傾向于被修改的現(xiàn)象提醒開(kāi)發(fā)人員,應(yīng)在開(kāi)發(fā)過(guò)程中重視代碼壞味的檢測(cè)和及時(shí)消除,以減少代碼壞味對(duì)后續(xù)的開(kāi)發(fā)與維護(hù)的不良影響.
基于第3.2節(jié)中代碼壞味與文件修改更為相關(guān)的結(jié)果,本節(jié)進(jìn)一步探究具體是哪一種或是哪幾種壞味對(duì)文件修改產(chǎn)生較大的影響.因此,分別計(jì)算包含某一種壞味的文件與不包含這種壞味的文件發(fā)生修改的占比.
在對(duì)壞味進(jìn)行檢測(cè)的過(guò)程中發(fā)現(xiàn),并非所有的13種壞味都存在于各個(gè)項(xiàng)目中.但能夠檢測(cè)到的壞味大多隨著版本的演化一直存在于項(xiàng)目中,即這些壞味存在于每個(gè)項(xiàng)目的所有版本中.為了保證研究的可靠性,本文僅對(duì)存在于該項(xiàng)目所有版本中的代碼壞味進(jìn)行研究.這是由于要探究特定類型的壞味與文件修改之間的相互關(guān)聯(lián),若是其中某一版本中不存在受某種壞味影響的文件,不僅實(shí)驗(yàn)結(jié)果會(huì)產(chǎn)生誤差,同時(shí)也因?yàn)椴淮嬖谠搲奈抖笔Я搜芯康囊饬x.
圖4展示了被特定類型壞味影響的文件與未被該壞味影響的文件發(fā)生修改的占比.圖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匯總了特定類型壞味與文件修改的關(guān)聯(lián)信息,其中,“√”表示該種壞味在對(duì)應(yīng)項(xiàng)目中表現(xiàn)出與文件修改有較大關(guān)聯(lián),“×”則表示關(guān)聯(lián)度較小,“-”表示未檢測(cè)到此種壞味.
Table 7 Correlations between code smell and file modification表7 壞味與文件修改的關(guān)聯(lián)
從圖4、表7中可以看出,所有項(xiàng)目中都包含被CC與LPL影響的文件,并且這些文件都有較大的被修改傾向;相反的,YC與文件修改不存在顯著性關(guān)聯(lián).至于AS,Bb和CP這幾種壞味,它們?cè)诓煌捻?xiàng)目中對(duì)文件修改產(chǎn)生的影響則不盡相同:在 Che、Egit、Xerces2-j、Jgit、Tomcat、Recommenders中,包含 Bb的文件有較大可能性被修改,而在Nifi中,Bb并不呈現(xiàn)這樣的特征;AS在Che中對(duì)文件修改的影響不大,而在Egit、Xerces2-j、Tomcat、Nifi、Recommenders中對(duì)文件的修改影響較大;Che、Jgit、Tomcat中,被 CP影響的文件發(fā)生修改的可能性不大,而另一些項(xiàng)目中被影響的文件有較大的可能性被修改.
進(jìn)一步地,我們使用 Wilcoxon符號(hào)秩檢驗(yàn)來(lái)探究包含特定類型壞味的文件是否與文件修改有著較強(qiáng)的相互關(guān)聯(lián).對(duì)于每一種特定類型的代碼壞味,設(shè)置原假設(shè)H04:包含該種壞味的文件發(fā)生修改的占比與不包含該壞味的文件發(fā)生修改的占比不存在顯著性差異.若檢驗(yàn)得到的p-value值小于0.05,將否定原假設(shè),并進(jìn)一步計(jì)算效應(yīng)值.表8展示了具體的p-value及效應(yīng)值,當(dāng)p-value值大于0.05時(shí),對(duì)應(yīng)數(shù)值加“*”表示.
Table 8 p-values and effect size表8 p-value值與效應(yīng)值
如表8所示,有5種代碼壞味需要引起我們的關(guān)注.具體來(lái)說(shuō),CC與LPL這2種壞味在所有8個(gè)項(xiàng)目中均有p-value值小于0.05,效應(yīng)值大于0.5,充分說(shuō)明這兩種壞味會(huì)對(duì)文件的修改產(chǎn)生較大的影響.AS,Bb,CP則并非存在于所有的項(xiàng)目中,且對(duì)不同項(xiàng)目中文件修改的影響也不盡相同.例如,AS在 Tomcat和 Recommenders中的p-value值大于0.05,表示該壞味在這兩個(gè)項(xiàng)目中與文件修改的關(guān)聯(lián)度不大;而在另外的項(xiàng)目中則顯示出較強(qiáng)的關(guān)聯(lián).類似地,Bb在Xerces2-j和Nifi這2個(gè)項(xiàng)目中,CP在Recommenders中的p-value值均大于0.05.
通過(guò)分析上述5種代碼壞味與文件修改之間的關(guān)聯(lián),我們發(fā)現(xiàn),CC,LPL與文件的修改之間存在較大的關(guān)聯(lián).另外,我們?nèi)孕瓒嗉雨P(guān)注 Bb,該壞味存在于除了 Jmeter以外的所有項(xiàng)目中,且在多個(gè)項(xiàng)目中還具有較大的效應(yīng)值,對(duì)文件修改的影響也相對(duì)較大.AS與CP若是存在于項(xiàng)目中,也需要引起一定的關(guān)注.
基于第 3.3節(jié)中壞味與文件修改之間關(guān)聯(lián)度的分析,本節(jié)計(jì)算受到 5種特定類型代碼壞味(AS,Bb,CP,CC,LPL)影響的共同文件在各自壞味文件中的占比.為了便于觀察與分析,表9僅列舉各個(gè)項(xiàng)目中根據(jù)公式(7)和公式(8)計(jì)算得到的重疊率均值大于0.1的情況.
Table 9 Overlaps between specific smelly files表9 不同壞味文件的重疊率
從表9可知,受CC與LPL影響的文件在所有項(xiàng)目中的均值都高于0.1,其中,同時(shí)包含這兩種壞味的文件在CC中的占比均值最低為0.165,最高為0.693;在LPL中的占比均值最低為0.135,最高為0.296.這表明CC與LPL這兩種壞味存在著共生現(xiàn)象,往往同時(shí)存在于同一項(xiàng)目中,且這兩種壞味文件有一定程度的重疊.這些現(xiàn)象與這兩種壞味的定義相一致,這兩種壞味分別代表了具有復(fù)雜圈復(fù)雜度、較多代碼行數(shù)和長(zhǎng)參數(shù)列表的代碼片段.類似地,AS與CP在6個(gè)項(xiàng)目中存在著共生現(xiàn)象.值得注意的是,這兩種壞味文件的重疊程度很大,在AS中的占比最高為0.924,在CP中的占比最高為0.956.這表明,項(xiàng)目中很大比例的文件同時(shí)受到AS與CP這兩種壞味的影響.
進(jìn)一步深入分析壞味文件的數(shù)量可以發(fā)現(xiàn):雖然CC與LPL的重疊率整體上并不是很高,但在所有項(xiàng)目中,被 CC與 LPL同時(shí)影響的文件數(shù)量較多,例如,Jgit中同時(shí)包含這兩種壞味的文件數(shù)量在各個(gè)版本中的均值為13,而其余壞味文件的重疊數(shù)量均值都低于3.對(duì)于AS與CP這兩種壞味,除了Che中同時(shí)被這兩種壞味影響的文件數(shù)量大于 65以外,其他項(xiàng)目中同時(shí)被這兩種壞味影響的文件數(shù)量較少,最高僅為 19.在少量重疊文件而高重疊率的情況下,同時(shí)對(duì)AS與CP這兩種壞味進(jìn)行代碼重構(gòu),消除壞味的影響將事半功倍.
此外,被CC與其他壞味共同影響的文件往往占據(jù)其他壞味文件較大的比例,例如AS_and_CC_in_AS在6個(gè)項(xiàng)目中重疊率高于0.1,而AS_and_CC_in_CC僅在2個(gè)項(xiàng)目中重疊率高于0.1.這說(shuō)明CC壞味文件數(shù)量往往大于AS壞味文件數(shù)量.除CC之外,LPL也具有類似的特征.因此,壞味文件數(shù)量較高的CC與LPL應(yīng)該在重構(gòu)時(shí)優(yōu)先考慮,以提高重構(gòu)效率.
通過(guò)上述觀察可知,在代碼重構(gòu)過(guò)程中,應(yīng)綜合考慮存在共生現(xiàn)象的CC與LPL、AS與CP壞味文件,力求共同消除.特別地,應(yīng)該提高CC和LPL這兩種壞味文件的維護(hù)優(yōu)先級(jí).此外,AS,Bb,CP這幾種壞味也不可忽視.
存在于各個(gè)項(xiàng)目中不同的壞味密度與活躍度的特征,促使我們探究壞味與文件改變之間的相互關(guān)聯(lián).正如代碼壞味的相關(guān)定義,壞味是指會(huì)影響代碼結(jié)構(gòu)與可理解性的不友好片段,我們的探究表明,壞味的存在會(huì)導(dǎo)致文件內(nèi)容發(fā)生修改,而不會(huì)影響到整個(gè)項(xiàng)目的基本框架.我們推測(cè),開(kāi)發(fā)者們?cè)陂_(kāi)發(fā)過(guò)程中更注重實(shí)現(xiàn)功能需求而忽視壞味的存在,往往會(huì)給后續(xù)的開(kāi)發(fā)與維護(hù)帶來(lái)一定的困難.從另一個(gè)角度來(lái)看,如果壞味問(wèn)題得到有效的解決,那么將極大地減少由壞味引起的文件修改的成本.
根據(jù)文件修改與具體壞味之間的關(guān)聯(lián)與包含不同壞味文件之間的重疊率,我們的研究提供給開(kāi)發(fā)者有關(guān)包含壞味文件維護(hù)的有效建議,如提升包含ComplexClass和LongParameterList這兩種壞味文件的維護(hù)優(yōu)先級(jí)、維護(hù)時(shí)可同時(shí)考慮消除AntiSingleton與ClassDataShouldBePrivate這兩種壞味的影響等,以實(shí)現(xiàn)更為高效的代碼維護(hù).
和多數(shù)的實(shí)證研究一樣,本文的工作也存在以下內(nèi)部與外部因素對(duì)可靠性的影響.
(1) 內(nèi)部影響:其中一個(gè)主要威脅是實(shí)驗(yàn)環(huán)境的準(zhǔn)確性.首先,我們使用DéCOR檢測(cè)代碼壞味,代碼壞味的定義本身具有一定的主觀性,這可能是一個(gè)對(duì)本研究可靠性的潛在威脅.但是作為一個(gè)被廣泛應(yīng)用的代碼壞味自動(dòng)檢測(cè)工具,DéCOR檢測(cè)壞味的召回率為 100%,平均準(zhǔn)確率為 60%,因此我們認(rèn)為,由DéCOR檢測(cè)的壞味是可以用于該實(shí)證研究的.另外,我們對(duì)實(shí)驗(yàn)環(huán)境進(jìn)行了仔細(xì)的檢查與測(cè)試,以保證實(shí)驗(yàn)的有效性.
(2) 外部影響:我們僅對(duì)8個(gè)Java項(xiàng)目進(jìn)行了實(shí)證研究.這些項(xiàng)目具有相對(duì)較大的規(guī)模并且應(yīng)用于多個(gè)領(lǐng)域,并且我們共計(jì)探究了 104個(gè)項(xiàng)目版本,從這些版本中得到的數(shù)據(jù)足夠支持我們的研究.我們相信,這樣的實(shí)驗(yàn)規(guī)模足以保證該研究的有效性.然而,對(duì)更大的系統(tǒng)進(jìn)行進(jìn)一步驗(yàn)證,可以幫助推廣我們的發(fā)現(xiàn).此外,我們的實(shí)驗(yàn)是在文件級(jí)別進(jìn)行的,將來(lái)我們可以將實(shí)驗(yàn)設(shè)置到類級(jí)別,以探索源代碼和壞味之間更精確的相關(guān)性.
本文系統(tǒng)開(kāi)展了關(guān)于代碼壞味對(duì)軟件演化影響的實(shí)證研究,分析了代碼壞味與源文件變更之間的相關(guān)性.文件的變更細(xì)化為新增文件、修改文件和移除文件這3類具體操作,目的是探究這些不同的操作類型是否均與壞味相關(guān).本文針對(duì)8個(gè)Java項(xiàng)目的104個(gè)版本,運(yùn)用DéCOR工具檢測(cè)13種不同類型的壞味,實(shí)驗(yàn)結(jié)果表明.
(1) 代碼壞味的密度和活躍度在不同的項(xiàng)目中呈現(xiàn)不同的特征,大部分項(xiàng)目的壞味密度隨著軟件的不斷演化呈現(xiàn)下降趨勢(shì),且壞味活躍度通常不高.
(2) 與不含壞味的文件相比,含有壞味的文件更容易被修改.此外,代碼壞味與文件的添加和移除沒(méi)有明顯的關(guān)聯(lián).
(3) ComplexClass和 LongParameterList這兩類壞味與文件的修改更為相關(guān);此外,如果項(xiàng)目中存在AntiSingleton、Blob和ClassDataShouldBePrivate這些壞味,仍然需要重視它們產(chǎn)生的影響.
(4) 軟件維護(hù)的過(guò)程中可考慮AntiSingleton和ClassDataShouldBePrivate這兩種壞味的特征同時(shí)進(jìn)行重構(gòu);包含ComplexClass和LongParameterList這兩種壞味的文件有較大的可能性包含其他壞味,且被這兩種壞味影響的文件重疊率較高、重疊文件數(shù)量較多,應(yīng)提高這兩種壞味的重構(gòu)優(yōu)先級(jí).
本文采用大規(guī)模的實(shí)證研究,深入分析了代碼壞味對(duì)軟件演化的影響,并為開(kāi)發(fā)人員在軟件維護(hù)過(guò)程中如何有效地重構(gòu)代碼給出了相關(guān)建議:開(kāi)發(fā)人員應(yīng)該更多地關(guān)注包含代碼壞味的文件,尤其是包含 ComplexClass或LongParameterList的文件,避免引入這些壞味,將極大地降低文件被修改的可能性,從而降低整個(gè)軟件生命周期中維護(hù)成本.在未來(lái)的工作中,將分析更多的項(xiàng)目,并在更為細(xì)化的級(jí)別上進(jìn)行深入的實(shí)證研究.此外,擬引入人的因素,如熟悉度、中心性和所有權(quán)等,以討論代碼壞味產(chǎn)生的影響.