軟體度量是評估系統品質的關鍵方法,透過多種指標,開發者能深入瞭解系統複雜度、耦合度及維護性。本文將探討軟體度量如何確保系統維護性,並介紹一些實用指標及應用,例如熱點分析識別高風險檔案、元件排名找出重要類別、LCOM4 評估類別內聚性等。此外,架構適應度函式提供客觀評估架構特性完整性的方法,確保系統符合預期設計原則。同時,目標-問題-指標法(GQM)則提供系統化框架,引導開發者將目標轉化為可衡量指標,提升架構品質。
軟體度量與系統維護性的深度探討
軟體度量(Software Metrics)是評估軟體系統品質的一個重要手段,透過各種度量指標,開發者可以更有效地理解系統的複雜度、耦合度以及維護性等關鍵特性。在本章中,我們將探討軟體度量在確保系統維護性方面的作用,並介紹一些實用的度量指標及其應用。
熱點分析與重構
在軟體開發過程中,複雜且頻繁變更的檔案往往是錯誤的源頭。這些檔案通常具有高度的耦合度和複雜性,對系統的維護性造成了嚴重的挑戰。透過熱點分析(Hotspot Analysis),開發者可以識別出這些問題檔案,並採取相應的重構措施來降低系統的複雜度。
創新性的視覺化工具可以簡化這一過程。例如,圖9-9展示了Apache Cassandra的「軟體城市」(Software City)視覺化呈現,該視覺化使用3D圖形同時展示了多個度量指標。
圖9-9:Sonargraph-Architect渲染的軟體城市
在這個視覺化中,每個建築物代表一個原始檔案。檔案按照模組、套件或名稱空間進行分組。建築物的佔地面積與檔案的行數(Lines of Code)成正比,而建築物的高度則反映了檔案的平均複雜度,顏色深淺則表示檔案在過去90天內的變更頻率。例如,高而深色的建築物通常是重構的良好候選物件。
圖表翻譯: 圖9-9中的每個建築物代表一個原始檔案,其大小和高度分別對應於檔案的行數和複雜度,顏色代表變更頻率。透過這個視覺化,開發者可以輕易識別出需要重構的檔案。
其他實用度量指標
除了耦合度和複雜度度量外,還有一些其他的度量指標對於軟體系統的分析非常有用。
元件排名(Component Rank)
元件排名是根據Google的頁面排名(Page Rank)演算法。它透過分析類別之間的依賴關係來找出系統中「重要」的類別。對於新加入專案的開發者來說,優先閱讀具有最高元件排名的類別是一個不錯的選擇,因為這些類別通常被其他多個類別參照。
// 示例:使用元件排名找出重要的類別
public class ComponentRankCalculator {
public void calculateComponentRank(List<ClassInfo> classes) {
// 實作元件排名演算法
for (ClassInfo classInfo : classes) {
// 分析類別的依賴關係
int rank = calculateRank(classInfo);
classInfo.setComponentRank(rank);
}
}
private int calculateRank(ClassInfo classInfo) {
// 根據依賴關係計算排名
// 省略具體實作
}
}
內容解密:
上述程式碼展示瞭如何計算類別的元件排名。首先,我們遍歷所有類別資訊(ClassInfo),然後對每個類別計算其元件排名。計算排名的具體方法取決於所使用的演算法,通常涉及分析類別之間的依賴關係。
LCOM4
LCOM4(Lack of Cohesion of Methods)是一種用於評估類別是否違反單一職責原則(Single-Responsibility Principle)的度量指標。它透過構建類別中方法與欄位之間的依賴圖來計算。如果一個類別的LCOM4值大於1,表示該類別可以被拆分成多個更小的類別,從而提高系統的可維護性。
// 示例:計算類別的LCOM4值
public class LCOM4Calculator {
public int calculateLCOM4(ClassInfo classInfo) {
// 構建方法與欄位之間的依賴圖
DependencyGraph graph = buildDependencyGraph(classInfo);
// 計算連通元件數量
return graph.getConnectedComponents();
}
private DependencyGraph buildDependencyGraph(ClassInfo classInfo) {
// 省略具體實作
// 構建類別中方法與欄位之間的依賴關係
}
}
內容解密:
上述程式碼用於計算類別的LCOM4值。首先,我們構建類別中方法與欄位之間的依賴圖,然後計算該圖中的連通元件數量。如果連通元件數量大於1,表示該類別可以被進一步拆分。
架構適應度函式(Architectural Fitness Functions)
架構適應度函式是由Neal Ford、Rebecca Parsons和Patrick Kua在其著作《Building Evolutionary Architectures》中首次提出的。它們提供了一種客觀評估架構特性完整性的方法。透過使用適應度函式,開發者可以自動化地檢查系統架構是否符合預期的設計原則,從而確保系統的可維護性和可演化性。
// 示例:使用架構適應度函式檢查架構完整性
public class ArchitecturalFitnessFunction {
public boolean checkArchitectureIntegrity(SystemInfo systemInfo) {
// 實作架構適應度函式
// 省略具體實作
return true; // 或 false,取決於檢查結果
}
}
內容解密:
上述程式碼展示瞭如何使用架構適應度函式檢查系統架構的完整性。具體實作取決於所使用的檢查標準和架構特性。
隨著軟體系統變得越來越複雜,軟體度量和架構分析的重要性將進一步提升。未來,我們可以期待看到更多創新性的度量指標和工具的出現,這些工具將更好地幫助開發者應對軟體系統的複雜性挑戰。同時,自動化和智慧化的分析工具也將成為軟體開發流程中的重要組成部分。
透過不斷改進軟體度量和分析方法,我們可以建立更具韌性和可維護性的軟體系統,以滿足不斷變化的業務需求和技術挑戰。最終,這將有助於提高軟體開發的效率和品質,從而為使用者提供更好的產品和服務。
為了進一步闡述上述觀點,我們將在下一章節中探討軟體度量在實際專案中的應用案例,並分析如何在不同的開發場景中有效地使用這些度量指標。
參考資料
- Neal Ford, Rebecca Parsons, and Patrick Kua. Building Evolutionary Architectures. O’Reilly Media.
- “PageRank,” Wikipedia, last updated April 9, 2022, https://oreil.ly/vrdFb.
附錄
軟體度量工具列表
- Sonargraph-Architect
- 其他軟體度量工具…
這些工具在軟體度量和分析中發揮著重要作用,幫助開發者更好地理解和管理軟體系統的複雜性。
軟體架構特性與可維護性量測
軟體架構特性(Architectural characteristics),也被稱為「-ilities」,是指軟體架構需要實作的目標,例如穩定性(stability)、可擴充套件性(scalability)、可維護性(maintainability)和敏捷性(agility)。適應度函式(Fitness functions)則用來衡量架構實作這些特性的程度。例如,可以使用生產資料(如同時線上使用者數量和平均回應時間)來衡量可擴充套件性,或者使用相對迴圈性(Relative Cyclicity)和可維護性水平(Maintainability Level)來衡量程式碼的可維護性。
軟體架構師的重要任務:權衡取捨
軟體架構師最重要的任務之一是進行權衡取捨。在一個軟體系統中,不可能同時實作所有理想的特性而不增加複雜性到無法管理的程度。因此,必須優先考慮最符合業務目標的特性,避免增加不必要的複雜性。更糟糕的是,有些特性無法同時滿足。例如,最大效能和最大安全性是相互對立的目標,因為安全性需要加密,而加密會消耗大量CPU資源。必須在兩者之間找到合適的平衡點。
最終,建議優先考慮最多三個特性,加上可維護性。很少有案例表明可維護性不重要。可以使用適當的適應度函式來衡量每個優先特性的實作程度。
使用軟體度量作為適應度函式
本章討論的某些軟體度量可以用作適應度函式來衡量可維護性,包括可理解性(comprehensibility)。例如:
- 可維護性水平(Maintainability Level)門檻值為75%或更高
- 相對迴圈性(Relative Cyclicity)在套件/名稱空間和元件層級上的門檻值,元件層級為4%或更低,套件/名稱空間層級為0%
- 元件的結構債務指數(Structural Debt Index)門檻值在低百位數
- 複雜度量可以用來計算原始檔中被視為複雜的比例。例如,可以定義平均縮排超過3、平均複雜度超過10或大小超過800行程式碼(Lines of Code)的檔案為複雜檔案。然後,將這些複雜檔案的程式碼行數加總,並與系統中的總程式碼行數進行比較。可以決定不讓超過10%的程式碼被視為複雜。
程式碼複雜度分析範例
public class ComplexClass {
// 示例方法,展示複雜度
public void complexMethod() {
// 複雜邏輯判斷
if (condition1 && condition2) {
for (int i = 0; i < 10; i++) {
// 處理邏輯
}
} else {
while (condition3) {
// 不同處理邏輯
}
}
}
}
內容解密:
上述範例程式碼展示了一個具有較高複雜度的類別和方法。在實際開發中,應盡量簡化邏輯判斷和迴圈結構,以提高程式碼的可讀性和可維護性。
如何持續追蹤軟體度量
要實作根據度量的反饋迴路,需要能夠持續追蹤軟體度量。最佳實踐是在自動化構建過程中每天收集度量資料,並將其輸入能夠持續追蹤的工具中。
一旦擁有趨勢資料,就可以將其繪製成圖表。例如,圖9-12顯示了最近90天內程式碼行數(Lines of Code)的增長情況。建議持續追蹤所有適應度函式,並結合一些耦合度和大小度量。
度量趨勢圖表示例
graph LR
A[開始追蹤] --> B[收集度量資料]
B --> C[分析趨勢]
C --> D[調整架構]
D --> B
圖表翻譯:
此圖示展示了持續追蹤軟體度量的流程。首先開始追蹤,然後每天收集度量資料,接著分析趨勢資料,並根據分析結果調整軟體架構,最後再回到收集資料的步驟,形成一個持續改進的迴路。
軟體開發的幾條黃金法則
以下是一些黃金法則,可以幫助防止結構侵蝕,確保軟體設計的模組化和可維護性:
- 建立正式且可執行的架構模型:定義軟體的不同部分以及它們之間的允許依賴關係。這有助於控制和最小化耦合度。
- 避免迴圈依賴:在名稱空間/套件層級避免迴圈依賴,在原始檔/類別層級限制迴圈依賴群組的大小。
- 避免程式碼重複:程式碼重複(透過複製和貼上進行程式設計)是一種典型的程式碼壞味道。雖然在某些情況下,重複程式碼可以用來打破難以解決的迴圈依賴,但這是一種罕見的特殊情況。
- 限制原始檔大小:將原始檔大小限制在800行程式碼以內(作為軟門檻)。
- 限制最大縮排和修改的迴圈複雜度:將最大縮排限制在4以內,將修改的迴圈複雜度(Modified Cyclomatic Complexity)限制在15以內(作為軟門檻)。
程式碼最佳實踐範例
public class BestPractice {
// 簡化方法範例
public void simpleMethod() {
// 簡潔的邏輯處理
if (simpleCondition) {
// 直接處理
}
}
}
內容解密:
上述範例展示瞭如何透過簡化邏輯判斷來提高程式碼的可讀性。實際開發中,應盡量保持方法簡潔明瞭,避免過於複雜的結構。
工具選擇與實施
有多種工具可供選擇,用於持續追蹤軟體度量和實施上述黃金法則。一些工具是免費的,例如SonarQube(針對某些語言),而其他工具如Sonargraph-Enterprise則提供更豐富的功能和自定義圖表。
最佳實踐是使用工具自動驗證上述黃金法則,並在CI構建過程中實施。這樣可以確保所有開發人員熟悉這些規則,從而提高程式碼的可維護性和品質。
以目標-問題-指標法提升軟體架構品質
軟體開發過程中,最重要的事情往往最難衡量。系統中的技術債有多少?應該在哪裡投入資源?架構是否滿足最重要的品質屬性?團隊的設計成熟度如何進展?面對這些大問題,人們很容易根據直覺進行猜測,但這種方法可能存在偏差且不可靠。更好的做法是使用資料進行分析。當遇到難以回答的問題時,目標-問題-指標(GQM)方法便成為一個有效的解決方案。
目標-問題-指標法的由來與原理
目標-問題-指標法是一種由 Victor Basili 和 David Weiss 提出的分析技術,用於幫助團隊確定如何衡量和評估軟體開發中的複雜問題。這個方法簡單易學,且可以單獨使用或作為協作工作坊的一部分。雖然發現合適的指標仍然需要創意和邏輯思維,但 GQM 提供了一個結構化的框架,幫助團隊獲得更好的結果。
GQM 的核心假設
GQM 的核心假設是:要衡量某件事情,必須先了解為什麼要衡量它。瞭解背後的目標和需要回答的問題,可以幫助我們選擇最合適的指標。那些瞭解指標背後原因的團隊,更有可能使用並信任這些指標來指導未來的決策。GQM 將模糊的目標轉化為可量化和驗證的模型。
GQM 模型的層次結構
GQM 模型採用層次結構,可以將其視為一棵樹:
- 目標是樹根
- 問題是從目標延伸出來的樹枝
- 指標則是回答問題所需的資料,位於樹葉上
這種結構確保了可追溯性:可以從任何一片樹葉(收集的資料)回溯到樹根(收集資料的目的)。
建立 GQM 樹
在 GQM 中,目標是一個簡單的陳述,描述了想要理解和衡量的內容。一個理想的目標陳述應該包含以下要素:
- 目的
- 要衡量的物件
- 感興趣的主題或問題
- 評估的角度
軟體架構相關的目標範例
以下是一些與軟體架構相關的目標範例:
- 從使用者的角度提升系統可用性
- 從產品經理的角度縮短新微服務的開發時間
- 從軟體開發者的角度減少架構中的技術債
- 減少上線的 Bug 數量
- 在使用者發現之前檢測出更多的生產環境問題
- 從使用者的角度提升機器學習模型的準確性
- 從開發團隊的角度做出更好的架構設計決策
定義目標的重要性
目標的定義至關重要,它為衡量活動提供了方向。目標可以聚焦於任何感興趣的物件,包括架構元素、軟體開發流程、技術實驗、設計檔案,甚至團隊和組織。在分析過程中,隨著對衡量物件的理解加深,目標可能會被調整。即使是一個不完美的目標陳述,只要它抓住了需要衡量的內容的精髓,也是有用的。
提出問題與定義指標
一旦定義了目標,接下來就需要提出問題來進一步闡述和評估目標。這些問題應該是操作性的,有助於評估與目標的接近程度。優秀的問題可以揭示問題並指出潛在的解決方案。提出好的問題需要好奇心和開放的思維,即使暫時不知道如何回答這些問題。
與系統可用性相關的問題範例
以下是一些與提升系統可用性相關的問題範例:
- 目前的系統可用性是多少?
- 哪些元件或服務的可用性最好?哪些最差?
- 哪些元件或服務最容易宕機?
- 元件或服務為什麼會不可用?
- 平均宕機時間是多久?
- 宕機通常發生在什麼時候?
定義指標
為了回答這些問題,需要定義相關的指標。指標可以有多種形式,包括簡單的評分標準、布林值、統計推斷以及各種複雜度的公式。任何計劃使用的指標最終都需要清晰、準確的定義。每個指標都與至少一個問題相關聯。同一個指標有時可以用來回答多個問題,而一個問題可能需要多個指標來回答。
GQM 樹的範例與實踐
將上述步驟結合起來,就可以建立一棵 GQM 樹,如圖 10-1 所示。GQM 的名稱簡明扼要地概括了這個方法的步驟:
- 確定目標
- 列出評估目標的問題
- 定義幫助回答問題的指標
圖 10-1:GQM 樹範例
graph LR
A[目標:提升系統可用性] --> B[問題:目前可用性是多少?]
A --> C[問題:哪些元件最容易宕機?]
B --> D[指標:系統可用性百分比]
C --> E[指標:元件宕機頻率]
D --> F[資料:系統執行時間記錄]
E --> G[資料:元件宕機記錄]
圖表翻譯: 此圖示展示了一個 GQM 樹的範例,從目標「提升系統可用性」出發,延伸出相關問題和指標,最終到達用於計算指標的資料層。
內容解密:
這個 GQM 樹範例展示瞭如何從一個高層次的目標開始,逐步細化到具體的問題和指標。透過這種結構化的分析方法,可以確保所收集的資料與最終目標保持一致,並且可以追溯到最初的目的。
在定義完指標之後,還需要決定如何收集計算指標所需的資料。這是 GQM 方法中的重要一步,因為資料的品質直接影響到指標的有效性和可靠性。
GQM 方法的優勢與實踐意義
GQM 方法提供了一個系統化的框架,幫助團隊將模糊的目標轉化為可衡量、可驗證的指標。透過這種方法,團隊可以更有效地評估進展,發現問題,並做出資料驅動的決策。在軟體架構評估、技術債管理、系統可用性提升等領域,GQM 都展現出了其獨特的價值。
案例研究:GQM 在軟體開發團隊中的應用
本章後續將透過一個案例研究,展示某個軟體開發團隊如何利用 GQM 方法評估和改進他們的架構。透過這個案例,讀者將更深入地瞭解如何在實際工作中應用 GQM 方法。
內容解密:
這個 Python 程式碼範例展示瞭如何計算系統可用性。函式 calculate_system_availability 接受一個系統執行時間記錄列表作為輸入,計算系統的可用性百分比。透過這個指標,可以評估系統的整體可用性,並據此進行改進。
隨著軟體系統變得越來越複雜,如何有效地衡量和評估系統品質成為了一個重要的課題。GQM 方法提供了一個有力的工具,幫助團隊應對這一挑戰。未來,我們可以期待看到更多根據 GQM 的創新實踐和工具出現,進一步提升軟體開發的效率和品質。