はじめに
自分でかいたコードや、他の人のコードレビューをするとき、「なんとなくみにくいコードだけど、どこまでリファクタリングすべき?」という疑問がよくでてきます。
仮に、コードのみにくさ、つまり複雑性を定量化できれば、「このコードの複雑性がxxになるまではリファクタリングしよう」という判断ができますよね。
そんな願いを叶えてくれる指標がMcCabeのサイクロマチック数という指標です。今回はPythonのコードを例にサイクロマチック数をつかって、コードの複雑性を評価したいと思います。
サイクロマチック数とは?
そもそも、サイクロマチック数とは、コードの複雑性を定量的に測定することのできる指標で、この指標の増加はテストすべき処理の増加を意味します。つまり、数値が大きいほど、コードの可読性、保守性が悪いということです。
目安としては、以下がよく使われるようです。
サイクロマチック数 | 評価 |
---|---|
1 - 10 | シンプルなプログラム、リスクが低い |
11 - 20 | より複雑、中程度のリスク |
21 - 50 | 複雑、高リスク |
> 50 | テスト不能なコード、非常に高リスク |
より具体的には、for, if, whileが多いほど数が増えていきます。条件式やループが多いコードにバグが多いのは、明白ですよね。
実際に使ってみる。
サイクロマチック数を測定するパッケージはいくつかありますが、今回はmccabパッケージを使っていきます。 サンプルコードとして、test1~7まで順に複雑にした関数と一つのクラスを準備します。
def test1(x): # シンプルプログラム print(x) def test2(x): # if分を追加 if x == 1: print("one") else: print("other") def test3(x): # 入れ子の関数を追加 def tmp(): ## print(x) tmp() ## if x == 1: print("one") else: print("other") def test4(x): # 条件分岐を追加 def tmp(): print(x) tmp() if x == 1: print("one") elif x == 2: ## print("two") else: print("other") def test5(x): # for文を追加 def tmp(): print(x) tmp() if x == 1: print("one") elif x == 2: print("two") else: print("other") for i in range(x): ## print(i) def test6(x): # 条件式を追加 def tmp(): print(x) tmp() if x == 1: print("one") elif x == 2 or x == 3: ## print("two, three") else: print("other") for i in range(x): print(i) def test7(x): ## for文を2重 def tmp(): print(x) tmp() if x == 1: print("one") elif x == 2 or x == 3: print("two, three") else: print("other") for i in range(x): for j in range(x): ## print(j, i) class Test: ## クラスを追加 def __init__(self, x): self.x = x def test(self): print(self.x)
このコードを例にサイクロマチック数を測定した結果が以下です。test5~test6では、if分の条件式をいじっただけですので、サイクロマチック数は増えていないようです。また、classの場合は、各メソッドごとに測定されていますね。
python -m mccabe sample_functions.py 1:0: 'test1' 1 5:0: 'test2' 2 12:0: 'test3' 3 24:0: 'test4' 4 38:0: 'test5' 5 55:0: 'test6' 5 72:0: 'test7' 6 91:4: 'Test.__init__' 1 94:4: 'Test.test' 1
# 表示する最小のサイクロマチック数を指定することも python -m mccabe --min 5 sample_functions.py 38:0: 'test5' 5 55:0: 'test6' 5 72:0: 'test7' 6
このように簡単にコードの複雑性を定量化することができます。
まとめ
コードのリファクタリングって、職人技で沼にハマりやすく無限に時間が使えるなと思っています。この指標をつかえば、各処理やスクリプトでどれぐらいのテストケースが必要なのか、どこまで処理をシンプルにすればよいかが定量化できるので、チーム内でのコミュニケーションにも使えそうですね。