同じメソッドを複数回呼び出すときの循環的な複雑さ


12

Code Reviewでの質問のおかげで、下のコードのCyclomatic Complexityが正確に何であるかについて少し意見の相違(本質的に何かを学ぶ機会)になりました。

public static void main(String[] args) {
    try {
        thro();
        thro();
        thro();
        thro();
        thro();
        thro();
        thro();
    }
    catch (NullPointerException e) {
    }
}

private static Random random = new Random();

public static void thro() throws NullPointerException {
    if (random.nextBoolean())
        throw new NullPointerException();
    System.out.println("No crash this time");
}

Eclipseでこのコードを記述し、Eclipseメトリックプラグインを使用すると、メインメソッドのMcCabe Cyclomatic Complexityは2であり、throメソッドの場合は2であることがわかります。

しかし、他の誰かが私に、thro複数回の呼び出しの複雑さはであると言っておりnumber of calls * method complexity、したがって、メインメソッドの複雑さは7 * 2 = 14であると主張しています。

異なるものを測定していますか?二人とも正しいことはできますか?または、実際のサイクロマティックの複雑さは何ですか?


5
パスは2つしかないため、関数のCC は2です。プログラムのCC は高くなっています。これは暗闇の中で完全な突き刺しですが、複雑なアプリケーション全体のCCを一度に計算することは実行不可能であるため、コード分析ソフトウェアは各機能を個別のブラックボックスと見なします。
-Phoshi

@Phoshi答えとしてそれを書いて、(可能であれば)2つの分離があることを示すリンクを提供するなら、私は喜んでその答えを受け入れます。
サイモンフォースバーグ

あなたはCCの測定で可能な例外に起因するすべてのパス、神の助けをカウントする場合は10の下に番号を取得するには、いくつかの些細なコードをリファクタリングで質問を男
mattnz

回答:


9

私はこれを正しく理解したときに、循環的複雑度のは、main8 -コードを介して直線的に独立したパスの数です。7行のいずれかで例外を受け取るか、まったく受け取らないか、1行を超えることはありません。考えられる「例外ポイント」のそれぞれは、コードを通る1つの異なるパスに正確に対応しています。

McCabeがそのメトリックを発明したとき、彼は例外処理を念頭に置いたプログラミング言語を持っていなかったと思います。


しかし、どの行が例外をスローするかは本当に重要ですか?
サイモンフォースバーグ

5
@SimonAndréForsberg:はい、そうです。「thro」は、呼び出されたときにグローバルカウンターをインクリメントするという副作用を持っていると考えてください(コードの可能なパスは変更されません)。そのカウンターの可能な結果は0〜7であるため、これはCCが少なくとも 8 あることを証明します。
DocBrown

私が使用しているメトリックプラグインがメソッドの誤った値を報告していると言いますmainか?
サイモンフォースバーグ

@SimonAndréForsberg:あなたのメトリックプラグインはわかりませんが、2は明らかに8ではありません。
Doc Brown

私の質問にプラグインメトリックへのリンクがあります....
サイモンForsbergの

6

「もう一人の男」なので、ここで答えて、私が言うことについて正確に説明します(他の形式については特に正確ではありませんでした)。

上記のコード例を使用して、循環的複雑度を8として計算し、その計算方法を示すコメントをコードに記載しています。パスを説明するために、すべてthro()呼び出しのループが「メイン」「コードパス」(または「CP = 1」)として成功したと考えます。

public static void main(String[] args) {
  try {
             // This is the 'main' Code Path: CP = 1
    thro();  // this has a branch, can succeed CP=1 or throw CP=2
    thro();  // this has a branch, can succeed CP=1 or throw CP=3
    thro();  // this has a branch, can succeed CP=1 or throw CP=4
    thro();  // this has a branch, can succeed CP=1 or throw CP=5
    thro();  // this has a branch, can succeed CP=1 or throw CP=6
    thro();  // this has a branch, can succeed CP=1 or throw CP=7
    thro();  // this has a branch, can succeed CP=1 or throw CP=8
  }
  catch (NullPointerException e) {
  }
}

したがって、このメインメソッドでは8つのコードパスをカウントしますが、これは8の循環的複雑度です。

Javaの用語では、関数を終了するための各メカニズムはその複雑さにカウントされるため、成功状態を持ち、たとえば最大3つの例外をスローするメソッドには、4つの文書化された出口パスがあります。

このような関数を呼び出すメソッドの複雑さは次のとおりです。

CC(method) = 1 + sum (methodCallComplexity - 1)

私が考える他のことは、私の意見では、catch句はメソッドの複雑さに寄与せcatch単純にthrowsブランチのターゲットであり、したがって複数throwのsカウントのターゲットであるcatchブロックであるということですthrowすべてに対して一度だけではなく、それぞれに対して。


OutOfMemoryExceptionsの可能な分岐も数えていますか?コードを分岐させる可能性がありますが、メトリックの有用性を薄めるため、誰もカウントしません。
テラスティン

いいえ、私はそうではありません...あなたは正しいですが、この引数のコンテキストでは、メソッドがスローするように宣言されている例外のみをカウントします。また、メソッドが3つの例外を宣言しているのに、callinchコードがcatch (Throwable t) {...それを行う場合、スローするよう宣言している例外の数は問題ではないと思います。
-rolfl
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.