循環的複雑度に関する一般的な質問があります。添付のソースコードをご覧ください。
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;
}
上記のコードから、ここに示すようにフローグラフを描きました。
そして、ノードの数を10と数え、エッジの数も10と数えました。次の数式を使用して、V(G) = E-N + 2P、3を計算し、循環的複雑度(またはV(G))としました。
私の計算は正しいですか?ありがとう!
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
_
これで、制御フローグラフを描画できます。
これはかなり複雑なグラフですが、ソースコードの外観にほぼ対応する平面グラフとしてレイアウトしようとしました。
グラフには多くの大きな違いがあります。例えば。ループから出るパスは1つだけです。これは、ループ条件とbreak
の両方がループを終了できることを無視します。そして、ループ本体に入ると、グラフは継続しません。
この関数のCCは4であることがわかります。数式を適用するか、手動で独立したパスを数えると、.
これは、ソフトウェア品質の測定基準としてのCCにも限界があることを示しています。あなたが示したコードは非常に複雑で、理解するのは簡単ではありません。それでも、これはこのはるかに単純なメソッドと同じCCを持っています(_&&
_は制御フロー演算子であることに注意してください)。
_boolean inBounds(int x, int y) {
return (0 <= x) && (x < 10)
&& (0 <= y) && (y < 10);
}
_