一般には条件分岐のネストが悪いと言われるけれど
コードの複雑性を議論するとき、よく条件分岐でネストされてて行数の多いコードは複雑、といわれることがある。 それ自体は確かに僕も複雑になるだろうなと思うし、それによってコードが読みにくくなるのはすごくあると思う。
ただ、それを解決するために条件分岐を過剰に減らしたり、行数を減らすためにロジックを過度に分けるのはどうなんだろう。 確かにロジックを切り出せば、挙動をそこの部分だけで検証しやすくなるし、その時点ではメリットが大きいかもしれない。でもそれを5年後に読んで読みやすいと思うだろうか。必ずしもそうとは限らないのではないか。
命名の善悪
モジュールやメソッド、ヘッダーファイル、ライブラリに代表されるロジックの切り出しは多くの場合命名を伴う。 自作のヘルパー、メソッド、とても結構。書いた本人はそのときは便利だと思って定義して使っているんだと思う。でもその命名って一度きりとか数回しか使われないんだったら、そもそもロジックを平易に元の場所に書いておいた方がわかりやすくないだろうか。命名されるとその命名はそのアプリケーションコードのドメインでしか利用できない独自知識となってしまう。その独自知識を覚えるまでにそれなりのコストがかかるのではないだろうか。本人でさえ、数年後には思い出しコスタが発生するはず。
ロジックの再利用率が複雑性を減らす
切り出し(あるいはそれに伴う命名)にも善悪があるという話を書いたが、過度な切り出しを避けるにはどうしたらいいか。個人的にはロジックの再利用率に目をつけてみるのが良いのではないかと思う。ロジックの再利用はメソッドやヘルパーがどれだけ呼び出されたか。と言った事や無名関数、ループがどれぐらい実行されたかというところで計測できそう。 何度も呼び出されるロジックが命名されているのは、その命名を覚えるに値するほどの再利用率があるという根拠があれば、命名もしやすくなるはず。
多角的な判断が必要
とはいえ、再利用率が高ければそれで良いのかというとそうじゃない。コードの再利用率が高いということはそれだけ単純なプログラムだと言えるし、単純なプログラムで出来ることは少ない。何度も再利用したいからと言って、簡単なロジックに命名しても仕方ない(簡単なロジックでも有用ならそれをライブラリにしてもっと知らしめて欲しい)。 他にも再利用のために過度に階層化した呼び出しをすると言ったことも考えられるけれど、これもネストと実質的にやっていることは同じなので複雑性の低下にはあまり意味は無い。 となるとロジック同士のグラフでエッジが少なくなればいいといった議論も出てきたり。 要するに、複雑性というカオスなものをある一定のルールで指標化するのはきっと難しい。この辺は次元圧縮とも似ているところがある。
人間的な判断もありかも。
1年に1度しか行かない定食屋で「いつものお願い」って注文するのは、定食屋さんに対して酷だ。 週に1度くらい通ってたら「いつものお願い」も使えるかもしれない。これは合理的な妥協点と言える。 かといって「いつものお願い」したいから毎日同じもの注文するのは何か違う。 こんな感じで、デファクトスタンダード的な指標が出てくるまでは、人間的な判断も時には良いかも。