軟件架構(gòu)的代碼質(zhì)量(使用耦合度量來支持系統(tǒng)架構(gòu))

本文將從兩個(gè)比較有趣的耦合度量開始,即傳入耦合傳出耦合食呻。這些基于整數(shù)的度量表示幾個(gè)相關(guān)對(duì)象(即相互協(xié)調(diào)以產(chǎn)生行為的對(duì)象)。任一度量中高數(shù)值表示架構(gòu)的維護(hù)問題:高傳入耦合表示對(duì)象具有太多職責(zé),而高傳出耦合表示對(duì)象不夠獨(dú)立蜂科。在文章中,我將介紹每個(gè)這樣的問題及其解決的方法短条。

傳入耦合

具有太多職責(zé)并非什么壞事导匣。例如,組件(或包)通常試圖用于整個(gè)架構(gòu)中茸时,這就會(huì)給它們帶來高傳入耦合值贡定。核心框架(如 Strut)、登錄包(如 log4j)之類的實(shí)用工具以及異常層次結(jié)構(gòu)通常具有高傳入耦合可都。

在圖 1 中缓待,可以看到一個(gè)包com.acme.ascp.exception具有一個(gè)值為 4 的傳入耦合。這并不奇怪渠牲,因?yàn)?code>web旋炒、daoutilfrmwrk包都希望利用一個(gè)公共的異城╄荆框架瘫镇。

圖 1. 傳入耦合的符號(hào)
傳入耦合的符號(hào)

如圖 1 所示,exception包具有一個(gè)值為 4 的傳入耦合(或者叫做Ca)答姥,這并非是件壞事铣除。異常層次結(jié)構(gòu)很少會(huì)出現(xiàn)很大的改變。監(jiān)視
exception包的傳入耦合是個(gè)好主意踢涌,然而通孽,由于徹底改變了這個(gè)包中的行為或契約,所以將引起它的四個(gè)依賴包全都出現(xiàn)連鎖反應(yīng)睁壁。

測(cè)量抽象性

通過進(jìn)一步檢查exception包并注意抽象到具體類的比率背苦,可以派生出另一個(gè)度量:抽象性互捌。在本例中,exception包具有零抽象性行剂,因?yàn)樗乃蓄惗际蔷唧w的秕噪。這與我前面的觀察是一致的:exception包中的高度具體性表示對(duì)exception作出的任何更改將影響所有相關(guān)包,即com.acme.ascp.frmwrk厚宰、com.acme.ascp.util腌巾、com.acme.ascp.daocom.acme.ascp.web

通過理解傳入耦合表示組件的職責(zé)铲觉,并持續(xù)監(jiān)視這個(gè)度量澈蝙,可以防止軟件架構(gòu)出現(xiàn)熵(entropy),即使在大多數(shù)設(shè)計(jì)良好的系統(tǒng)中也很容易出現(xiàn)熵撵幽。

支持設(shè)計(jì)靈活性

很多架構(gòu)設(shè)計(jì)在利用第三方包時(shí)都考慮到了靈活性灯荧。獲得靈活性最好是通過使用接口來防止架構(gòu)在第三方包中發(fā)生更改。例如盐杂,系統(tǒng)設(shè)計(jì)師可以創(chuàng)建一個(gè)內(nèi)部接口包來利用第三方記帳代碼逗载,但是只對(duì)這些使用記帳代碼的包公開接口。順便說一下链烈,這與 JDBC 的工作原理類似厉斟。

圖 2. 通過設(shè)計(jì)獲得靈活性
acme.ascp 應(yīng)用程序與第三方記帳包相耦合

如圖 2 所示,acme.ascp 應(yīng)用程序通過com.acme.ascp.billing包與第三方記帳包相耦合强衡。這創(chuàng)建了一定級(jí)別的靈活性:如果有了另一個(gè)第三方記帳包更加有利用價(jià)值擦秽,那么應(yīng)該只有一個(gè)包會(huì)受到變更的影響。此外食侮,com.acme.ascp.billing的抽象性值是 0.8号涯,這表明它可以通過接口和抽象類來防止被修改。

如果要轉(zhuǎn)換到第三方實(shí)現(xiàn)锯七,只需要對(duì)com.acme.ascp.billing包進(jìn)行重構(gòu)链快。更好的方法是:通過在設(shè)計(jì)中考慮靈活性以及了解變更的隱含意義,可以通過開發(fā)人員測(cè)試來防止修改所造成的任何損害眉尸。

在對(duì)內(nèi)部記帳包作出變更前域蜗,您可以分析代碼覆蓋率報(bào)告,以確定是否有任何測(cè)試真正

測(cè)試了這個(gè)包噪猾。找到一些級(jí)別的覆蓋率后霉祸,您可以更仔細(xì)地檢查這些測(cè)試案例來驗(yàn)證它們是否足夠了。如果未找到覆蓋率袱蜡,您將會(huì)知道丝蹭,關(guān)閉并插入新庫(kù)的努力將更具風(fēng)險(xiǎn)性并可能花費(fèi)更長(zhǎng)的時(shí)間。

使用代碼度量收集所有這些似乎正確的信息非常容易坪蚁。另一方面奔穿,如果您根本不了解與測(cè)試覆蓋率相關(guān)的包耦合的知識(shí)镜沽,那么為替換第三方庫(kù)確定的時(shí)間最多就是個(gè)猜測(cè)!

監(jiān)視熵

前面提到過贱田,即使計(jì)劃得最好的架構(gòu)也會(huì)出現(xiàn)熵缅茉。通過團(tuán)隊(duì)磨損或未充分記錄的意圖沟涨,沒有經(jīng)驗(yàn)的開發(fā)人員可能會(huì)疏忽地導(dǎo)入似乎有用的包迅涮,不久以后禁荸,系統(tǒng)傳入耦合的值將開始增長(zhǎng)普舆。

例如,將圖 3 與 圖 2 進(jìn)行比較瑞驱。您注意到架構(gòu)增加的脆弱性了嗎荧恍?現(xiàn)在不僅dao包直接利用第三方記帳包伸刃,而且另一個(gè)甚至不想直接使用任何記帳代碼的包也引用了這兩個(gè)記帳包乔询!

圖 3. 出現(xiàn)代碼熵
出現(xiàn)代碼熵

試圖為另一個(gè)包關(guān)閉com.third.party.billing包確實(shí)很具有挑戰(zhàn)性蔬蕊!設(shè)想一下降低產(chǎn)生缺陷和中斷系統(tǒng)各種行為方面的風(fēng)險(xiǎn)所需的測(cè)試腳手架。事實(shí)上哥谷,像這樣的架構(gòu)很少變更,因?yàn)樗鼈儫o法支持修改麻献。更糟糕的是们妥,即使像對(duì)現(xiàn)有組件的升級(jí)這樣的重要修改也會(huì)導(dǎo)致整個(gè)代碼基中斷的事情出現(xiàn)。

傳出耦合

如果傳入耦合是一些依賴于某個(gè)特定組件的組件的話勉吻,那么傳出耦合則是某個(gè)特定組件所依賴的一些組件监婶。可以把傳出耦合看作傳入耦合的逆轉(zhuǎn)齿桃。

對(duì)于更改如何影響代碼來說惑惶,傳出耦合的引號(hào)意義與傳入耦合的類似。例如短纵,圖 4 描述了com.acme.ascp.dao包带污,它具有一個(gè)值為 3 的傳出耦合(或者叫做Ce):

圖 4. dao 包中的傳出耦合
dao 包中的傳出耦合

如圖 4 所示,com.acme.ascp.dao包依賴于org.apache.log4j香到、com.acme.ascp.utilcom.acme.ascp.exception組件來履行其行為契約鱼冀。與傳入耦合中一樣,依賴性級(jí)別本身并不是什么壞事悠就。重要的是您對(duì)耦合的了解以及耦合如何影響對(duì)相關(guān)組件的更改千绪。與傳入耦合一樣,抽象性度量在傳出耦合中起作用梗脾。在 圖 4 中荸型,com.acme.ascp.dao包完全是具體的;因此它的抽象性為 0炸茧。這表示其傳出耦合包com.acme.ascp.dao
的組件自己會(huì)變得脆弱瑞妇,因?yàn)?code>com.acme.ascp.dao包與 3 個(gè)附加的包具有傳出耦合稿静。如果它們中的一個(gè)(比如說com.acme.ascp.util)發(fā)生更改,將會(huì)在com.acme.ascp.dao中發(fā)生連鎖反應(yīng)踪宠。因?yàn)?code>dao無法通過接口或抽象類隱藏注入細(xì)節(jié)自赔,所以任何更改都可能影響它的依賴組件。

耦合與覆蓋相加等于……

檢查傳出耦合的關(guān)系數(shù)據(jù)柳琢,并將其與代碼覆蓋相關(guān)聯(lián)绍妨,會(huì)促進(jìn)作出更明智的決策。例如柬脸,假設(shè)一個(gè)新的需求傳達(dá)給開發(fā)團(tuán)隊(duì)他去。您可以將與該需求相關(guān)的更改精確到圖 4 所示的com.acme.ascp.util包。而且倒堕,在以前幾個(gè)版本中灾测,依賴于util并且具有 0 抽象性的dao包具有很多高優(yōu)先權(quán)的缺陷(非常可能是因?yàn)閷?duì)這個(gè)包進(jìn)行的開發(fā)人員測(cè)試太有限垦巴,有意思的是媳搪,最大的可能是由于代碼中的高復(fù)雜性值引起的)。在這種情況下骤宣,您的優(yōu)勢(shì)是了解com.acme.ascp.utilcom.acme.ascp.dao之間的關(guān)系秦爆。知道dao包依賴于util這一事實(shí)告訴您,為支持新需求而在util中進(jìn)行的任何修改可能會(huì)對(duì)易出故障的dao包產(chǎn)生負(fù)面的影響憔披!

看到這個(gè)鏈接將幫助進(jìn)行風(fēng)險(xiǎn)評(píng)估等限,甚至幫助進(jìn)行工作級(jí)別的分析。如果未注意到這個(gè)鏈接芬膝,您可能已經(jīng)猜到將需要快速編碼工作來支持新需求望门。如果已經(jīng)看到這個(gè)鏈接,就可以分配適當(dāng)?shù)臅r(shí)間和資源來降低dao包中的間接損害锰霜。

監(jiān)視依賴性

正像連續(xù)地監(jiān)視傳入耦合可以揭示架構(gòu)設(shè)計(jì)中的熵一樣筹误,監(jiān)視傳出耦合也有助于發(fā)現(xiàn)不必要的依賴性。例如癣缅,在圖 5 中纫事,似乎在一些地方有人決定
com.acme.ascp.web包要為com.acme.ascp.user提供內(nèi)容。在user
包中的某處所灸,一個(gè)或多個(gè)對(duì)象正在實(shí)際從web包導(dǎo)入一個(gè)對(duì)象丽惶。

圖 5. user 包中的傳出耦合
user 包中的傳出耦合

很明顯,這并非架構(gòu)設(shè)計(jì)最初意圖爬立。但是钾唬,由于您一直針對(duì)傳出耦合而監(jiān)視系統(tǒng),所以可以輕松地重構(gòu)并改正這些不一致÷崭眩或許奕巍,web包中有用的實(shí)用工具對(duì)象應(yīng)該移動(dòng)到實(shí)用工具包,以便其他包可以利用它而不會(huì)引起不必要的依賴性儒士。

測(cè)量不穩(wěn)定性

您可以將系統(tǒng)的傳出耦合和傳入耦合的數(shù)量結(jié)合起來的止,形成另一個(gè)度量:不穩(wěn)定性。通過將傳出耦合除以傳出傳入耦合的和(Ce / (Ca + Ce))着撩,可以生成一個(gè)比率诅福,表示一個(gè)穩(wěn)定的包(值接近于 0)或者不穩(wěn)定的包(值接近于 1)。如這個(gè)等式所示拖叙,傳出耦合對(duì)包的穩(wěn)定性起作用:一個(gè)包越依賴于其他包氓润,面對(duì)更改時(shí)它越容易受到連鎖反應(yīng)的影響。反過來說薯鳍,一個(gè)包越被依賴咖气,它越不可能發(fā)生更改。

例如挖滤,在圖 5 中崩溪,user包的不穩(wěn)定性值為 1,這表示它有一個(gè)值為 4 的傳出耦合斩松,而沒有傳入耦合悯舟。像com.acme.ascp.dao 這樣的包中的更改將會(huì)影響user包。

在設(shè)計(jì)和實(shí)現(xiàn)架構(gòu)時(shí)砸民,依賴于穩(wěn)定的包是有益的,因?yàn)檫@些包不太可能更改奋救。類似地岭参,不穩(wěn)定的包依賴性會(huì)在發(fā)生更改時(shí)增大架構(gòu)內(nèi)發(fā)生間接損害的風(fēng)險(xiǎn)。

到主序列的距離

到目前為止尝艘,我已經(jīng)介紹了傳入耦合和傳出耦合演侯,前者可用來評(píng)估更改包造成的影響,后者可用來評(píng)估外界的更改如何影響包背亥。我還談及了抽象性度量和不穩(wěn)定性度量秒际,前者在您想要了解如何輕松地對(duì)包進(jìn)行修改時(shí)很有用,后者可用來了解包依賴性如何影響某個(gè)特定的包狡汉。

還可以使用另一個(gè)度量來了解影響軟件架構(gòu)的因素娄徊。這個(gè)度量通過 X, Y 坐標(biāo)上的一條直線來平衡抽象性和不穩(wěn)定性。主序列是笛卡兒坐標(biāo)上從X=0Y=1X=1Y=0的一條直線盾戴,如圖 6 所示:

圖 6. 主序列
主序列

通過沿著這條直線繪制包并測(cè)量它們到主序列的距離寄锐,可以推斷包的平衡。如果包對(duì)于抽象性和不穩(wěn)定性是平衡的,它的距離就接近于 0橄仆,如果包不平衡剩膘,那么它距離主序列的距離就接近于 1,如圖 7 所示:

圖 7. 到主序列的距離
到主序列的距離

檢查到主序列的距離

度量會(huì)產(chǎn)生有趣的結(jié)果盆顾。例如怠褐,上面的user包生成的值為 0。就一個(gè)實(shí)現(xiàn)包來說您宪,這個(gè)包是平衡的奈懒,即高度不穩(wěn)定。

總體來說蚕涤,“到主序列的距離” 度量嘗試補(bǔ)償實(shí)際實(shí)現(xiàn)筐赔。沒有代碼基包含所有抽象性和不穩(wěn)定性值為 1 或 0 的包 —— 多數(shù)包的值位于這兩個(gè)數(shù)字之間。通過監(jiān)視 “到主序列的距離” 度量揖铜,可以衡量包是否正在變得不平衡茴丰。尋找偏遠(yuǎn)的值,如值接近于 1 的包(這表示它們距離主序列盡可能地遠(yuǎn))天吓,有助于了解特定的不平衡如何影響架構(gòu)的可維護(hù)性(例如贿肩,通過脆弱性)。

結(jié)束語(yǔ)

在本期文章中龄寞,您已經(jīng)了解幾種可以持續(xù)監(jiān)視的架構(gòu)度量汰规。通過代碼分析工具可以報(bào)告?zhèn)魅牒蛡鞒鲴詈稀⒉环€(wěn)定性物邑、抽象性溜哮、到主序列的距離,這些代碼分析工具包括 JDepend色解、JarAnalyzer 和 Metrics plug-in for Eclipse監(jiān)視系統(tǒng)的代碼耦合度量有助于您掌握可破壞架構(gòu)的常見趨勢(shì)茂嗓,即設(shè)計(jì)剛度、包熵和不必要的依賴性科阎。此外述吸,根據(jù)抽象性和不穩(wěn)定性來測(cè)量系統(tǒng)平衡將使您不斷的了解系統(tǒng)的可維護(hù)性。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锣笨,一起剝皮案震驚了整個(gè)濱河市蝌矛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌错英,老刑警劉巖入撒,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異椭岩,居然都是意外死亡衅金,警方通過查閱死者的電腦和手機(jī)噪伊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來氮唯,“玉大人鉴吹,你說我怎么就攤上這事〕土穑” “怎么了豆励?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)瞒渠。 經(jīng)常有香客問我良蒸,道長(zhǎng),這世上最難降的妖魔是什么伍玖? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任嫩痰,我火速辦了婚禮,結(jié)果婚禮上窍箍,老公的妹妹穿的比我還像新娘串纺。我一直安慰自己,他們只是感情好椰棘,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布纺棺。 她就那樣靜靜地躺著,像睡著了一般邪狞。 火紅的嫁衣襯著肌膚如雪祷蝌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天帆卓,我揣著相機(jī)與錄音巨朦,去河邊找鬼。 笑死剑令,一個(gè)胖子當(dāng)著我的面吹牛糊啡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播尚洽,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼靶累!你這毒婦竟也來了腺毫?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤挣柬,失蹤者是張志新(化名)和其女友劉穎潮酒,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體邪蛔,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡急黎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片勃教。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡淤击,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出故源,到底是詐尸還是另有隱情污抬,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布绳军,位于F島的核電站印机,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏门驾。R本人自食惡果不足惜射赛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望奶是。 院中可真熱鬧楣责,春花似錦、人聲如沸诫隅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)逐纬。三九已至蛔屹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間豁生,已是汗流浹背兔毒。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留甸箱,地道東北人育叁。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像芍殖,于是被迫代替她去往敵國(guó)和親豪嗽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理豌骏,服務(wù)發(fā)現(xiàn)龟梦,斷路器,智...
    卡卡羅2017閱讀 134,599評(píng)論 18 139
  • //我所經(jīng)歷的大數(shù)據(jù)平臺(tái)發(fā)展史(三):互聯(lián)網(wǎng)時(shí)代 ? 上篇http://www.infoq.com/cn/arti...
    葡萄喃喃囈語(yǔ)閱讀 51,183評(píng)論 10 200
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法窃躲,類相關(guān)的語(yǔ)法计贰,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法蒂窒,異常的語(yǔ)法躁倒,線程的語(yǔ)...
    子非魚_t_閱讀 31,581評(píng)論 18 399
  • 對(duì)自己的職業(yè) 人生極其困惑 不想和人或數(shù)據(jù)打交道 根本沒有可以做的工作吧 對(duì)華潤(rùn)的簡(jiǎn)陋的辦公環(huán)境的抵觸 對(duì)精致生活...
    角落蜷縮閱讀 95評(píng)論 0 0
  • 你說你也不知道生活的意義是什么荞怒,但是生活是過出來的,不是想出來的秧秉,活著活著我們就會(huì)明白了褐桌。你說去擁抱大自然,這...
    會(huì)飛的豬123fly閱讀 531評(píng)論 0 0