追求代碼質量(3): 軟件架構的代碼質量

jopen 9年前發布 | 38K 次閱讀 代碼質量 代碼分析/審查/優化

原文出處: IBM中國

上一期文章中,我展示了如何使用代碼度量來評估代碼質量。盡管在那一期介紹的圈復雜度針對低級細節,如方法中執行路徑的數量,但其他類型的度量針對的是代碼的更高級方面。在本期文章中,我將展示如何使用各種耦合度量 來分析和支持軟件架構。

我將從兩個比較有趣的耦合度量開始,即傳入耦合傳出耦合。這些基于整數的度量表示幾個相關對象(即相互協調以產生行為的對象)。任一度量中高數值表示架構的維護問題:高傳入耦合表示對象具有太多職責,而高傳出耦合表示對象不夠獨立。在本期文章中,我將介紹每個這樣的問題及其解決的方法。

傳入耦合

具有太多職責并非什么壞事。例如,組件(或包)通常試圖用于整個架構中,這就會給它們帶來高傳入耦合值。核心框架(如 Strut)、登錄包(如 log4j)之類的實用工具以及異常層次結構通常具有高傳入耦合。

在圖 1 中,可以看到一個包com.acme.ascp.exception具有一個值為 4 的傳入耦合。這并不奇怪,因為web、dao、util和frmwrk包都希望利用一個公共的異常框架。

圖 1. 傳入耦合的符號

 追求代碼質量(3): 軟件架構的代碼質量

如圖 1 所示,exception包具有一個值為 4 的傳入耦合(或者叫做 Ca),這并非是件壞事。異常層次結構很少會出現很大的改變。監視exception包的傳入耦合是個好主意,然而,由于徹底改變了這個包中的行為或契約,所以將引起它的四個依賴包全都出現連鎖反應。

測量抽象性

通過進一步檢查exception包并注意抽象到具體類的比率,可以派生出另一個度量:抽象性。在本例中,exception包具有零抽象性,因為它的所有類都是具體的。這與我前面的觀察是一致的:exception包中的高度具體性表示對exception作出的任何更改將影響所有相關包,即com.acme.ascp.frmwrk、com.acme.ascp.util、com.acme.ascp.dao和com.acme.ascp.web。

通過理解傳入耦合表示組件的職責,并持續監視這個度量,可以防止軟件架構出現熵(entropy),即使在大多數設計良好的系統中也很容易出現熵。

支持設計靈活性

很多架構設計在利用第三方包時都考慮到了靈活性。獲得靈活性最好是通過使用接口來防止架構在第三方包中發生更改。例如,系統設計師可以創建一個內部接口包 來利用第三方記帳代碼,但是只對這些使用記帳代碼的包公開接口。順便說一下,這與 JDBC 的工作原理類似。

圖 2. 通過設計獲得靈活性

 追求代碼質量(3): 軟件架構的代碼質量

如圖 2 所示,acme.ascp 應用程序通過com.acme.ascp.billing包與第三方記帳包相耦合。這創建了一定級別的靈活性:如果有了另一個第三方記帳包更加有利用價值,那么應該只有一個包會受到變更的影響。此外,com.acme.ascp.billing的抽象性值是 0.8,這表明它可以通過接口和抽象類來防止被修改。

如果要轉換到第三方實現,只需要對com.acme.ascp.billing包進行重構。更好的方法是:通過在設計中考慮靈活性以及了解變更的隱含意義,可以通過開發人員測試來防止修改所造成的任何損害。

在對內部記帳包作出變更前,您可以分析代碼覆蓋率報告,以確定是否有任何測試真正 測試了這個包。找到一些級別的覆蓋率后,您可以更仔細地檢查這些測試案例來驗證它們是否足夠了。如果未找到覆蓋率,您將會知道,關閉并插入新庫的努力將更具風險性并可能花費更長的時間。

使用代碼度量收集所有這些似乎正確的信息非常容易。另一方面,如果您根本不了解與測試覆蓋率相關的包耦合的知識,那么為替換第三方庫確定的時間最多就是個猜測!

監視熵

前面提到過,即使計劃得最好的架構也會出現熵。通過團隊磨損或未充分記錄的意圖,沒有經驗的開發人員可能會疏忽地導入似乎有用的包,不久以后,系統傳入耦合的值將開始增長。

例如,將圖 3 與 圖 2 進行比較。您注意到架構增加的脆弱性了嗎?現在不僅dao包直接利用第三方記帳包,而且另一個甚至不想直接使用任何記帳代碼的包也引用了這兩個記帳包!

圖 3. 出現代碼熵

 追求代碼質量(3): 軟件架構的代碼質量

試圖為另一個包關閉com.third.party.billing包確實很具有挑戰性!設想一下降低產生缺陷和中斷系統各種行為方面的風險所需的測試腳手架。事實上,像這樣的架構很少變更,因為它們無法支持修改。更糟糕的是,即使像對現有組件的升級這樣的重要修改也會導致整個代碼基中斷的事情出現。

傳出耦合

如果傳入耦合是一些依賴于某個特定組件的組件的話,那么傳出耦合則是某個特定組件所依賴的一些組件。可以把傳出耦合看作傳入耦合的逆轉

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

圖 4. dao 包中的傳出耦合

 追求代碼質量(3): 軟件架構的代碼質量

如圖 4 所示,com.acme.ascp.dao包依賴于org.apache.log4j、com.acme.ascp.util和com.acme.ascp.exception組件來履行其行為契約。與傳入耦合中一樣,依賴性級別本身并不是什么壞事。重要的是您對耦合的了解以及耦合如何影響對相關組件的更改。

與傳入耦合一樣,抽象性度量在傳出耦合中起作用。在 圖 4 中,com.acme.ascp.dao包完全是具體的;因此它的抽象性為 0。這表示其傳出耦合包含com.acme.ascp.dao的組件自己會變得脆弱,因為com.acme.ascp.dao包與 3 個附加的包具有傳出耦合。如果它們中的一個(比如說com.acme.ascp.util)發生更改,將會在com.acme.ascp.dao中發生連鎖反應。因為dao無法通過接口或抽象類隱藏注入細節,所以任何更改都可能影響它的依賴組件。

耦合與覆蓋相加等于……

檢查傳出耦合的關系數據,并將其與代碼覆蓋相關聯,會促進作出更明智的決策。例如,假設一個新的需求傳達給開發團隊。您可以將與該需求相關的更改精確到圖 4 所示的com.acme.ascp.util包。而且,在以前幾個版本中,依賴于util并且具有 0 抽象性的dao包具有很多高優先權的缺陷(非常可能是因為對這個包進行的開發人員測試太有限,有意思的是,最大的可能是由于代碼中的高復雜性值引起的)。

在這種情況下,您的優勢是了解com.acme.ascp.util和com.acme.ascp.dao之間的關系。知道dao包依賴于util這一事實告訴您,為支持新需求而在util中進行的任何修改可能會對易出故障的dao包產生負面的影響!

看到這個鏈接將幫助進行風險評估,甚至幫助進行工作級別的分析。如果未注意到這個鏈接,您可能已經猜到將需要快速編碼工作來支持新需求。如果已經看到這個鏈接,就可以分配適當的時間和資源來降低dao包中的間接損害。

監視依賴性

正像連續地監視傳入耦合可以揭示架構設計中的熵一樣,監視傳出耦合也有助于發現不必要的依賴性。例如,在圖 5 中,似乎在一些地方有人決定com.acme.ascp.web包要為com.acme.ascp.user提供內容。在user包中的某處,一個或多個對象正在實際從web包導入一個對象。

圖 5. user 包中的傳出耦合

 追求代碼質量(3): 軟件架構的代碼質量

很明顯,這并非架構設計最初意圖。但是,由于您一直針對傳出耦合而監視系統,所以可以輕松地重構并改正這些不一致。或許,web包中有用的實用工具對象應該移動到實用工具包,以便其他包可以利用它而不會引起不必要的依賴性。

測量不穩定性

您可以將系統的傳出耦合和傳入耦合的數量結合起來,形成另一個度量:不穩定性。通過將傳出耦合除以傳出傳入耦合的和(Ce / (Ca + Ce)),可以生成一個比率,表示一個穩定的包(值接近于 0)或者不穩定的包(值接近于 1)。如這個等式所示,傳出耦合對包的穩定性起作用:一個包越依賴于其他包,面對更改時它越容易受到連鎖反應的影響。反過來說,一個包越被依賴,它越不可能發生更改。

例如,在圖 5 中,user包的不穩定性值為 1,這表示它有一個值為 4 的傳出耦合,而沒有傳入耦合。像com.acme.ascp.dao這樣的包中的更改將會 影響user包。

在設計和實現架構時,依賴于穩定的包是有益的,因為這些包不太可能更改。類似地,不穩定的包依賴性會在發生更改時增大架構內發生間接損害的風險。

到主序列的距離

到目前為止,我已經介紹了傳入耦合和傳出耦合,前者可用來評估更改包造成的影響,后者可用來評估外界的更改如何影響包。我還談及了抽象性度量和不穩定性度量,前者在您想要了解如何輕松地對包進行修改時很有用,后者可用來了解包依賴性如何影響某個特定的包。

還可以使用另一個度量來了解影響軟件架構的因素。這個度量通過 X, Y 坐標上的一條直線來平衡抽象性和不穩定性。主序列 是笛卡兒坐標上從X=0和Y=1到X=1和Y=0的一條直線,如圖 6 所示:

圖 6. 主序列

 追求代碼質量(3): 軟件架構的代碼質量

通過沿著這條直線繪制包并測量它們到主序列的距離,可以推斷包的平衡。如果包對于抽象性和不穩定性是平衡的,它的距離就接近于 0,如果包不平衡,那么它距離主序列的距離就接近于 1,如圖 7 所示:

圖 7. 到主序列的距離

 追求代碼質量(3): 軟件架構的代碼質量

檢查到主序列的距離 度量會產生有趣的結果。例如,上面的user包生成的值為 0。就一個實現包來說,這個包是平衡的,即高度不穩定。

總體來說,“到主序列的距離” 度量嘗試補償實際實現。沒有代碼基包含所有抽象性和不穩定性值為 1 或 0 的包 —— 多數包的值位于這兩個數字之間。通過監視 “到主序列的距離” 度量,可以衡量包是否正在變得不平衡。尋找偏遠的值,如值接近于 1 的包(這表示它們距離主序列盡可能地遠),有助于了解特定的不平衡如何影響架構的可維護性(例如,通過脆弱性)。

結束語

在本期文章中,您已經了解幾種可以持續監視的架構度量。通過代碼分析工具可以報告傳入和傳出耦合、不穩定性、抽象性、到主序列的距離,這些代碼分析工具包括 JDepend、JarAnalyzer 和 Metrics plug-in for Eclipse(參見 參考資料)。監視系統的代碼耦合度量有助于您掌握可破壞架構的常見趨勢,即設計剛度、包熵和不必要的依賴性。此外,根據抽象性和不穩定性來測量系統平衡將使您不斷的了解系統的可維護性。

參考資料

學習

獲得產品和技術

  • JDepend:生成設計質量度量的 Java 包依賴性分析器。
  • JarAnalyzer:這個工具分析 jar 文件之間的關系。
  • Metrics plug-in for Eclipse:計算圈復雜度和其他與代碼復雜性以及耦合相關的度量。

討論

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!