前回は、保守容易性指数 について説明をしました。
今回も、個々のメトリックス値がどのように変化していくのか、をテーマに サイクロマティック複雑度 について説明します。
Explanation
おさらいですが、サイクロマティック複雑度はMSDNのコード メトリックス値というページでの説明によると、
サイクロマティック複雑度 - コードの構造上の複雑さを測定します。
これは、プログラムのフローにある、異なるコード パスの数を計算することで作成されます。
複雑な制御フローが含まれるプログラムでは、十分なコード カバレッジを実現するためにより多くのテストが必要となり、保守性が低下します。
と定義されています。
制御フローとは、if、switch、do、while、foreach、for の6つのステートメントを指します。
サイクロマティック複雑度は数値が低い程良好であることを示します。
また、サイクロマティック複雑度の増加は、保守容易性指数が低下と、テストケースの増加による作業工数の増加を招きますね。
では、サイクロマティック複雑度はどういう風に算出されているのでしょうか? 今回も簡単ではありますが、サイクロマティック複雑度が変化する様子をソースコードを用いて観察してみましょう。
Exercise
今回はステートメント毎に6つのメソッドに分離しました。
1 | using System; |
(2012.05.01 23:49 上記ソースが一部文字化けしていたのを修正しました。)
今回もサンプルとしては良いかもしれませんが…的なソースですが、これを Metrics.exe で計測してみましょう。
その結果が下記になります。
メトリックス項目 | Case1 | Case2 | Case3 | Case4 | Case5 | Case6 |
---|---|---|---|---|---|---|
MaintainabilityIndex (保守容易性指数) |
71 | 66 | 67 | 67 | 78 | 70 |
CyclomaticComplexity (サイクロマティック複雑度) |
4 | 7 | 4 | 4 | 2 | 4 |
ClassCoupling (クラス結合度) |
2 | 2 | 2 | 2 | 1 | 1 |
DepthOfInheritance (継承の深さ) |
- | - | - | - | - | - |
LinesOfCode (コード行) |
5 | 8 | 7 | 7 | 3 | 5 |
分岐ステートメントがあると サイクロマティック複雑度 が1増加するのではなく、そこに含まれる条件式の個数分だけ増加するようです。
たとえば、Case6メソッドの2つめのfor文には2つの条件式があるので、最初のfor文と併せて、1+1+2=4とサイクロマティック複雑度が計算されています。
では、条件式が含まれれば、サイクロマティック複雑度が増加するかというとそうではないようです。
1 | using System; |
上記のソースコード中のCase1メソッドは、foreach以外の全ての分岐ステートメントを含んでいますが Metrics.exe で計測してみても、サイクロマティック複雑度は1のままです。
つまり、「分岐が発生する」とサイクロマティック複雑度が1増加するため、分岐が発生し得ない、つまり必ず通る、通らないような条件式はカウントされません。
(foreach文は、要素列挙の終了条件がコレクション内部に隠蔽されているため、foraech文があるだけで+1されてしまうようです)
Conclusion
まとめると、サイクロマティック複雑度は、
- 最も良好な値(分岐がない場合)は 1 であり、サイクロマティック複雑度の数値に上限はない。
- 分岐ステートメントに含まれる条件式が1つ増える毎に +1 される。逆に分岐ステートメントがあっても、条件式が存在しない、または常に条件が同じであるような場合は +1 されない。
- foreach文は存在するだけで常に +1 される。なぜならforeach文の内部に入るかどうかは、列挙されるコレクションの実装に依存するため、それ自体が条件式になってしまうから。
サイクロマティック複雑度を低下させるには、条件式を見直したり、条件式をメソッドとして抽出して、1つのメソッドに分岐を偏らせないようにする、等の方法が考えられます。
今回も長くなりましたが、サイクロマティック複雑度がどういうものかわかりました。
というわけで次回は 継承の深さ について、ソースコードを交えながら説明します。