背景
- 仕事でコーディングをしていた際に、上長から複雑度について指摘された
- 循環的複雑度という単語は知らなかったが、なんとなくバグが潜みそうなのはわかっていた
- せっかくなので、循環的複雑度について調べてみた
- 僕と同じくらいの経験が浅い人が対象
循環的複雑度とは?
とりあえず調べてみた、、、
循環的複雑度(英: Cyclomatic complexity)とは、ソフトウェア測定法の一種である。 Thomas McCabe が開発したもので、プログラムの複雑度を測るのに使われる。 プログラムのソースコードから、線形的に独立した経路の数を直接数える。wikipedia
要するに
- コードの中にif/else・for文・switch文などの分岐がいくつ含まれているかを計算する
- その数値の大小により、バグの混入しやすさがわかるというもの
具体的な数え方
- 何も分岐がないプログラムは「1」
- 分岐(if/else・for文・switch文・while文)がある度に「1」を加算する
一般的な考え
諸説あるみたいですが、、、
循環的複雑度 | 状態 |
---|---|
1-10 | 安全なコードでテストしやすい |
11-20 | 少し複雑なコード |
21-40 | 複雑なコードでテストが難しくなる |
41以上 | やばいやつ。テスト不可能 |
普通に書いていれば、10を超えることはなかなかないと思います。(私の場合、ビジネスロジックのところで20を計測したことが1度ありました)
実際に例を用いて考える
以下に適当なサービスを用意してみました。あくまでも、例なので処理自体に意味はないので、無視してください。
private String familyService(List<String> nameList) {
String id;
for (String name : nameList) {
if (name.startsWith("a")){
id = "a";
} else if (name.startsWith("b")){
id = "b";
} else if (name.startsWith("c")){
id = "c";
} else if (name.startsWith("d")){
id = "d";
} else if (name.startsWith("e")){
id = "e";
} else if (name.startsWith("f")){
id = "f";
} else if (name.startsWith("g")){
id = "g";
} else if (name.startsWith("h")){
id = "h";
} else {
id = "xxxx";
}
}
return id;
}
上の例ですと、for文が1つとif/elseの分岐が9つあるので、1+1+9で複雑度は11になります。
対策
循環的複雑度を下げる方法はいくつかあると思います。
- if文の条件を見直す
- 共通化できるものは切り出す
- クラスやメソッドとして新たに切り出す
- メソッドの責務を見直す
などなど、やり方はいくらでもあると思うので、どちらかというと普段からコーディングする際に循環的複雑度を意識することが大事なのだと思います。
私、個人としては循環的複雑度について調べたおかげで目視で計算できるようになったのですが、全員が全員そうではないと思うので、そういった方のために、IntelliJ IDEAの機能を紹介しておきます。
用意したもの
- IntelliJ IDEA
やりかた
- IntelliJ >> preference を選択
- 「overly complex」で検索する
- クラス・メソッド単位で設定する
設定値を超えるとこんな感じに怒られます。
まとめ
- 循環的複雑度の考え方は簡単
- 一度理解しておけば、リファクタリングやレビューのタイミングで役に立つ
参考にしたサイト
循環的複雑度 - Wikipedia
Learn Mccabe's Cyclomatic Complexity with Example