現役組み込みソフトエンジニアの青葉です。
良いコード・悪いコードを定量的に評価する仕組みとしてコードメトリクスというものがあります。その中でも代表的なサイクロマティック複雑度(循環的複雑度,Cyclomatic Complexity)についてメトリクスって何?という初学者向けに説明します。
この記事を一通り読めばサイクロマティック複雑度の計算方法と目安を理解できると思います。
サイクロマティック複雑度の計測方法
「グラフによって作られる領域の数」、「アーク数 ー ノード数+2」、あるいは「判定の数+1」
ソフトウェア工学(近代科学社)より
教科書的な言い方をするとサイクロマティック複雑度の計測方法は3種類ありますが、どれも結果として求まる数値は同じです。この記事では3つ目の「判定の数+1」のパターンで説明をします。早速ですがこの「判定の数+1」で求めるサイクロマティック複雑度はものすごく簡単で、1つの関数に含まれるIf/else・for・switch/case・while文の数によって計測できます。計測方法は以下の通りです。
- 1関数あたり初期値が1
- 分岐コード(If/else・for・switch/case・while)ごとに1加算
このたった2つの条件で計測されます。実際の例を交えてみていきましょう。
void function()
// 関数定義 複雑度 +1
{
xxxxxx;
}
上記のような何も分岐を含まない関数ではサイクロマティック複雑度は1となります。
void function()
// 関数定義 複雑度 +1
{
if(条件式)
// if 複雑度 +1
{
処理 ;
}
}
このように関数内に1つif文がある場合はサイクロマティック複雑度は2となります。
void function()
// 関数定義 複雑度 +1
{
for(初期化;条件式;ループ処理)
// for 複雑度 +1
{
処理;
if(条件式)
// if 複雑度 +1
{
処理 ;
}
else if(条件式)
// else if 複雑度 +1
{
処理 ;
}
else
// else 複雑度増加無し +0
{
処理 ;
}
}
}
今度は少し複雑な例を見てみましょう。例3の関数はサイクロマティック複雑度は4です。考え方のポイントは2つです。
- forループの中にあるif文でもサイクロマティック複雑度の加算方法は変わらないこと
- else ifはサイクロマティック複雑度は増加するがelseだけなら増加しないこと
void function()
// 関数定義 複雑度 +1
{
switch(変数名)
// switch 複雑度増加無し +0
{
case 値1:
// case 複雑度 +1
処理 ;
break;
case 値2:
// case 複雑度 +1
処理 ;
break;
default:
// default 複雑度増加無し +0
処理 ;
break;
}
}
例4はswitch/case文を含む関数ですが、こちらのサイクロマティック複雑度は3です。ポイントはcase文の数だけサイクロマティック複雑度が加算されるが、defaultは加算無しであることです。
サイクロマティック複雑度の目安
上記の計測方法を読んでこられた方なら既に理解されていると思いますが、サイクロマティック複雑度は数値が小さいほど適切で大きいほど不適切な設計あることを表します。一般的な目安は以下のようなものがあります。
サイクロマティック複雑度 | 複雑さの状態 | バグ混入確率 |
10以下 | 非常に良い構造 | 25% |
30以上 | 構造的なリスクあり | 40% |
50以上 | テスト不可能 | 70% |
75以上 | いかなる変更も誤修正を生む | 98% |
50以上では恐ろしいことが書いてありますね。なぜこのような見解になるかというと、関数を設計するときは設計の正しさを確かめるために「すべての分岐を通過できるか」等のテストをするのが一般的です。サイクロマティック複雑度50以上=50以上の分岐がある関数ということになりますから、50の分岐をちゃんと通過するか確かめるテストを実施する必要があり、そのためのテストケースを作らなければなりません。これはかなり骨が折れる作業になります。
分岐が50以上あって読みにくいうえに、膨大なテストをしなければならない関数に変更を加えようなんて、考えたくないですね。つまりサイクロマティック複雑度が大きいということは使いまわしの効かない再利用性の低いコードとも言えます。逆にこのサイクロマティック複雑度を低く保つことで長期的に生産性の高い開発が可能になります。
まとめ
今回は代表的なコードメトリクスであるサイクロマティック複雑度について説明してみました。「サイクロマティック」なんて難しい名前に対して計測方法は案外簡単で拍子抜けしたのではないでしょうか。サイクロマティック複雑度で示したいことをものすごくざっくりまとめるとif文やfor文がたくさん含まれる関数は変更しにくいしテストしにくいからやめた方が良いということです。
今回の記事では計測方法を紹介しましたが、こちらの記事ではサイクロマティック複雑度の下げ方についてまとめています。
コメント