web-dev-qa-db-ja.com

循環的複雑度の質問

循環的複雑度に関する一般的な質問があります。添付のソースコードをご覧ください。

private void downShift(int index)
{
    // index of "child", which will be either index * 2 or index * 2 + 1
    int childIndex;

    // temp storage for item at index where shifting begins
    Comparable temp = theItems[index];

    // shift items, as needed
    while (index * 2 <= theSize)
    {
        // set childIndex to "left" child
        childIndex = index * 2;

        // move to "right" child if "right" child < "left" child
        if (childIndex != theSize && theItems[childIndex + 1].compareTo(theItems[childIndex]) < 0)
            childIndex++;

        if (theItems[childIndex].compareTo(temp) < 0)
        {
        // shift "child" down if child < temp
            theItems[index] = theItems[childIndex];
        }
        else
        {
            // shifting complete
            break;
        }

        // increment index
        index = childIndex;
    }

    // position item that was originally at index where shifting began
    theItems[index] = temp;
}

上記のコードから、ここに示すようにフローグラフを描きました。

enter image description here

そして、ノードの数を10と数え、エッジの数も10と数えました。次の数式を使用して、V(G) = E-N + 2P、3を計算し、循環的複雑度(またはV(G))としました。

私の計算は正しいですか?ありがとう!

3
Al-geBra

2つのターミナルノード9と12が表示されているため、制御フローグラフが正しいとは思いません。代わりに、リターン用に1つのターミナルノードが必要です。

コードの難しさは、循環的複雑度を計算するための無関係な詳細が多く含まれていることです。それで、関連する制御フローにそれを取り除きましょう:

_f() {
    ...;                  // enter
    while (condWhile) {   // whilecond
        ...;              // whilebody
        if (condIncr)
            ...;          // incr
        if (condNoBreak)  // ifbreak
            ...;          // whileend
        else
            break;
        ...;              // whileend
    }
    ...;                  // end
}
_

制御フローif (cond) { a(); } else { break; } b();if (!cond) { break; } a(); b();と同等であることは注目に値します。これを念頭に置いて、コードを基本ブロックに分割できます。基本ブロックはジャンプターゲットで始まり、別の基本ブロックへのジャンプまたは分岐で終わります。

ソースコードをより基本的なブロックに分割した場合、それは問題ではありません。不要なブロックが1つのエッジと1つの頂点を制御フローグラフに追加し、お互いを相殺するため、それでも同じ複雑さにつながります。ここに私が選んだアセンブリスタイルの基本的なブロックがあります:

_ENTER:
  ...
  jump WHILECOND
WHILECOND:
  branch condWhile ? WHILE : END
WHILEBODY:
  ...
  branch condIncr ? INCR : IFBREAK
INCR:
  ...
  jump IFBREAK
IFBREAK:
  branch condNoBreak ? WHILEEND : END
WHILEEND:
  ...
  ...
  jump WHILECOND
END:
  ...
  return
_

これで、制御フローグラフを描画できます。

control flow graph

これはかなり複雑なグラフですが、ソースコードの外観にほぼ対応する平面グラフとしてレイアウトしようとしました。

グラフには多くの大きな違いがあります。例えば。ループから出るパスは1つだけです。これは、ループ条件とbreakの両方がループを終了できることを無視します。そして、ループ本体に入ると、グラフは継続しません。

この関数のCCは4であることがわかります。数式を適用するか、手動で独立したパスを数えると、.

これは、ソフトウェア品質の測定基準としてのCCにも限界があることを示しています。あなたが示したコードは非常に複雑で、理解するのは簡単ではありません。それでも、これはこのはるかに単純なメソッドと同じCCを持っています(_&&_は制御フロー演算子であることに注意してください)。

_boolean inBounds(int x, int y) {
  return (0 <= x) && (x < 10)
      && (0 <= y) && (y < 10);
}
_
4
amon