📝

コードの複雑度を測りたい

2024/12/12に公開

リファクタリングの優先度の指標のひとつとして、またそのリファクタリング後の結果を測定する指標のひとつとして定量的に複雑度を測りたくなって、SonarQube を導入したのでそのメモを残します。

複雑度

測りたい複雑度についての確認。

循環的複雑度(Cyclomatic Complexity)

プログラムの中にどれだけの分岐やループがあるかを数えて、コードの複雑さを測る指標のこと。分岐やループが増えるほど、コードが複雑で理解やテストが難しくなる傾向になる。

認知的複雑度(Cognitive Complexity)

プログラムを読む人の認知負荷を測るための指標のこと。コードの構造が「どれだけ頭を使う必要があるか」を定量的に評価する。循環的複雑度とは異なり、制御構造のネストやコードの流れの読みやすさに重点を置いている。

サンプルコードで違いを理解する

public class ComplexityExample {
    public void exampleMethod(boolean a, boolean b, boolean c) {
        if (a) {                 // 分岐1
            if (b) {             // 分岐2 (ネスト開始)
                for (int i = 0; i < 10; i++) { // 分岐3 (ループ開始)
                    if (c) {     // 分岐4 (さらにネスト)
                        System.out.println("Nested Block");
                    }
                }
            }
        } else {                 // 分岐5
            System.out.println("Else Block");
        }
    }
}
コードの要素 循環的複雑度 認知的複雑度
if (a) +1 +1
ネストした if (b) +1 +2
ネストした for ループ +1 +3
さらにネストした if (c) +1 +4
else(ネスト外) +1 +1
合計 6 = 5 + 1 11

SonarQube による循環的複雑度の算出方法

以下を参照。

cyclomaticComplexity = 1 + numberOfConditionalBranches

https://docs.sonarsource.com/sonarqube-server/10.6/user-guide/code-metrics/metrics-definition/#cyclomatic-complexity

SonarQube による認知的複雑度の算出方法

循環的複雑度のようなガイドは見つけられなかったが、以下に計算方法が記載されていた。
https://www.sonarsource.com/blog/cognitive-complexity-because-testability-understandability/

ガイドライン

この数値の上限におけるガイドラインの定義はなさそうだが、ChatGPT に聞いた結果は以下の通りだった。

循環的複雑度

複雑度の範囲 意味
1~10 低複雑度:管理しやすく、問題なし。
11~20 中程度の複雑度:リファクタリングを検討。
21~50 高複雑度:リファクタリングが強く推奨される。
51以上 非常に高い複雑度:コードを分割する必要がある。

認知的複雑度

複雑度の範囲 意味
0~4 理解が非常に容易:リファクタリング不要。
5~14 中程度の難易度:改善が必要な場合もある。
15以上 理解が困難:コードの簡素化を検討するべき。

この基準は、McCabe’s Cyclomatic Complexity の理論や SonarSource の公式資料 に基づいています。プロジェクトに応じてこれらの基準を調整することをおすすめします。

計測ツールの導入

昔に使ったことのあった SonarQube を使うことにした。

まず公式サイトに記載されている From the Docker image を実行。
すんなり SonarQube が立ち上がったように見えるが、なんか不安定かも。
後続の SonarScanner CLI を実行したときコードの分析ができなかった。
ログを確認するとメモリ不足により強制終了されているようなので、 sonar.properties をいじってみたが解決できず...。

ここで時間を使いたくないので、上記公式サイトに記載の別の方法 From the zip file を実行。
こちらも難なく SonarQube が立ち上がった。要求される Java のバージョンが 17 なのには注意。

続いてコードの分析をするために Analyzing a project を実行。
ここの 4 番目で SonarScanner CLI が必要になる。

Select your project's main language under Run analysis on your project, and follow the instructions to analyze your project. Here you'll download and execute a scanner on your code (if you're using Maven or Gradle, the scanner is automatically downloaded).

SonarQube の画面の指示に従ってこのページを確認。
ここでも from the Docker image があるが、前述のとおりうまくいかなかったので from the zip file を選択。
SonarScanner CLI を実行したところ、

ERROR Failed to parse Node.js version, got 'Couldn't find the Node.js binary. Ensure you have installed.'`

と怒られたので Node.js をインストールして、SonarScanner CLI を再実行。

INFO SonarScanner Engine completed successfully
INFO EXECUTION SUCCESS

とでたので成功!
無事に SonarQube から複雑度の確認ができるようになった!

Discussion