1 引言
軟體應用在發展到適當時機,”重構”,是開發過程中不可避免需要進行的一項工作。重構程式碼,以適配當前模組設計之初未考慮到的多樣化場景,並增加模組的可維護性、健壯性、可測試性。那麼,如何明確重構的方向,以及量化重構的結果呢?程式碼圈複雜度可以是一個供選擇的指標。下文介紹如何獲取應用的程式碼圈複雜度做到線上監控,給到覆盤程式複雜程度的資料支撐。
2 背景知識
2.1 圈複雜度
圈複雜度(Cyclomatic complexity,簡寫CC)也稱為條件複雜度,是一種程式碼複雜度的衡量標準。由托馬斯·J·麥凱布(Thomas J. McCabe, Sr.)於1976年提出,用來表示程式的複雜度,其符號為VG或是M。它可以用來衡量一個模組判定結構的複雜程度,數量上表現為獨立現行路徑條數,也可理解為覆蓋所有的可能情況最少使用的測試用例數。圈複雜度大說明程式程式碼的判斷邏輯複雜,可能質量低且難於測試和維護。程式的可能錯誤和高的圈複雜度有著很大關係。
2.2 圈複雜度計算方式
常用結構圈複雜度計算
- 順序結構:順序結構複雜度為1。
- if-else-else、switch-case:每增加一個分支,複雜度增加1,&& 、|| 運算也為一個分支。
- 迴圈結構:增加一個迴圈結構,複雜度增加1。
- return:增加一條return語句,複雜度將加1。
2.3 圈複雜度度量標準
如上列出行業內相對認可的度量資料,實際這個完全是看自己的業務體量和專案情況來決定的。假設你的業務很簡單,而且是個單體應用,功能都是很簡單的CRUD,那你的圈複雜度即使想上去也沒有那麼容易。此時你就可以選擇把圈複雜度的重構閾值設定為10.
假設你的業務十分複雜,而且涉及到多個其他的微服務系統呼叫,再加上各種業務中的corner case的判斷,圈複雜度上100可能都不在話下。
2.4 降低圈複雜度方法
1)函式提煉與拆分,單一職責
- 拆分成子函式
- 每個函式要有明確的功能實現,不要為了追求行數少而合併功能實現
- 邏輯模組和資料模組要區分開編寫
2)最佳化演算法
- 減少不必要條件、迴圈分支,儘量少用 if …else … ,採用三元表示式替換if else
3)表示式邏輯最佳化
4)減少提前return
3 方案概述
3.1 指令碼設計
1)開發語言
2)依賴環境
- lizard
- APScheduler
- smtplib
- pymysql
3)指令碼架構
3.2 功能介紹
1)支援檢索語言範圍:
支援15種開發語言,包含常用語言如下
- C/C++ (works with C++14)
- Java
- C# (C Sharp)
- JavaScript (With ES6 and JSX)
- Python
- Golang
2)掃描引數配置說明:
利用lizard執行掃描,常用命令如下:
配置檢查範圍:
- 列出要分析的程式語言。如果留空,將搜尋支援的所有語言。
-l LANGUAGES, --languages LANGUAGES
- 排除與模式匹配的檔案。匹配一切?匹配任何單個字元,“/folder/”遞迴地排除資料夾中的所有內容。可以指定多個模式。不要忘了在模式周圍加“”號。
-x EXCLUDE, --exclude EXCLUDE
- 設定白名單, 預設’./whitelizard.txt’
-W WHITELIST, --whitelist WHITELIST
配置閥值警告:
- 圈複雜度數警告的閾值,預設值為15,>15會產生警告。
-C CCN, --CCN CCN
- 設定欄位的限制數。可以程式碼行數,圈複雜度,令牌數,引數數或自定義欄位。如果函式設定超過了限制數會報警。
-T THRESHOLDS, --Threshold THRESHOLDS
配置報告輸出:
-o OUTPUT_FILE, --output_file OUTPUT_FILE
官網地址:http://www.lizard.ws
原始碼地址:https://github.com/terryyin/lizard
3)定時執行掃描任務:
- 透過BackgroundScheduler建立排程任務,自動觸發掃描方法,結果寫庫
def dojob(): scheduler = BackgroundScheduler() scheduler.add_job(func, "cron", hour=21, minute=30) scheduler.start()
3.3 結果展示
3.3.1 報告名詞解釋
- Cyclomatic complexity,圈複雜度也就是分支複雜度,最好保持在15 以下,目前指令碼設定閥值10。
- LOC,包含註釋的程式碼行數,目前設定200閥值。
- Token count ,token的個數,一個程式最多可以有 8192 個令牌, 每個令牌都是一個詞,例如關鍵字,識別符號,常量,標點符號,運算子。 對括號和字串計數作為 1 個令牌。 逗號、句點、LOCAL、分號、END 和註釋不計算在內。
- Parameter count,引數統計就是函式的引數個數,目前指令碼設定閥值10。
3.3.2 執行結果展示
- Windows環境執行指令碼,輸入file_root(檔案地址)執行掃描,支援自動彈出瀏覽器展示本次執行的Html報告
- 每週定期執行,按照系統維度掃描,支援觸發郵件通知對應系統研發檢視超過閥值方法名稱
3.3.3 應用資料監控
- 每週定期拉取指定分支最新程式碼,執行檔案分析,儲存掃描結果,透過資料圖表展示
4 總結
對於軟體程式碼好壞的衡量,圈複雜度可以作為一個參考指標,研發可以透過提煉拆分函式、最佳化演算法、最佳化邏輯表示式等方法降低模組(函式)圈複雜度。以上闡述圈複雜度一種線上監控方法,利用好線上化資料,結合現有團隊專案情況,才能形成更好的實踐機制。