2015年11月4日水曜日

サイクロマティック複雑度

循環的複雑度(Cyclomatic complexity
≒対象関数に分岐がどれだけあるかの度合い
≒C1(分岐網羅)の経路数

if文やfor文のような分岐のないソースコードの場合、複雑度は1。

コードに1つのif文が含まれていれば、コードには2つの分岐がある事になる。
一方はif文での条件が真となる場合の経路で、
もう一方はそれが偽となる場合の経路。複雑度は2。

では、以下のメソッドの複雑度は?

void TestFunc(bool condition1, bool condition2)
{
    if ((condition1 == true) && (condition2 == true))
    {
        std::cout << "Hoge" << std::endl;
    }
}




SourceMonitorの結果を見ると、複雑度は「3」。
??という事はC1(分岐網羅)ではなく、C2(条件網羅)?

/* ------------------ Source Monitor のHelpより抜粋 -------------------- */
The complexity metric is counted approximately as defined by Steve McConnell in his book Code Complete, Microsoft Press, 1993, p.395. The complexity metric measures the number of execution paths through a function or method. Each function or method has a complexity of one plus one for each branch statement such as if, else, for, foreach, or while. Arithmetic if statements (MyBoolean ? ValueIfTrue : ValueIfFalse) each add one count to the complexity total. A complexity count is added for each '&&' and '||' in the logic within if, for, while or similar logic statements.
Switch statements add complexity counts for each exit from a case (due to a break, goto, return, throw, continue, or similar statement), and one count is added for a default case even if one is not present. (Note: when a project's Modified Complexity option is selected, switch statements add a count of one to the complexity and the internal case statements do not contribute to the complexity metric.) Each catch or except statement in a try block (but not the try or finally statements) each add one count to the complexity as well.
/* ------------------------------------------------------------------ */

つまり、if文内の'&&'や'||'もカウントしているため、単に≒C1という訳ではない。

ユニットテストのTC数が十分か否かを確認するために、上記指標を用いる場合は、求められているカバレッジ(C0,C1,C2)とツールが叩き出す値の意味を考慮した上で利用する必要がある。


ちなみに循環的複雑度はグラフ理論における"Cyclomatic number"を応用したもの。

制御フローをグラフで表現した場合、
 複雑度 = エッジ(枝)の数 - ノード(節点)の数 + 2
  ⇒グラフによって区切られた空間の数(分割領域数)に等しくなる。

 ノード : 文、式、または制御合流点
       (ステートメントの数)
 エッジ : ノード間の遷移を表す有向辺
       (分岐が一つもなければ「ノードの数-1」となる)

以下の関数だと、ノードの数は7で、これらを結ぶエッジの数は8(※)。
よって、複雑度は8-7+2=3となる。


void TestFunc(bool condition1, bool condition2)
{
    int a = 1; //. ノード1
    if (condition1 == true) //. ノード2
    {
        int a = 1; //. ノード3
    }
    int a = 1; //. ノード4
    while (condition2 == true) //. ノード5
    {
        int a=1; //. ノード6
    }
    int a = 1; //. ノード7
}

(※)"⇒"の数がエッジ数
(condition1==true && condition2==true) ・・・ まずは全てのノードを通るパス
 ノード1⇒ノード2⇒ノード3⇒ノード4⇒ノード5⇒ノード6⇒ノード7
(condition1==false)
 ノード2⇒ノード4
(condition2==false)
 ノード5⇒ノード7


[参考資料]

・ソフトウェア工学III - プロダクトデータの解析II

0 件のコメント:

コメントを投稿