ごちゃごちゃとしたコードは読みにくく、バグを発見しづらいものです。ふつう、コードというものは書くよりも読む回数の方が圧倒的に多いので、ごちゃごちゃとした部分を減らすことで「読みやすさ」を最適化しなければなりません。読みやすいコードほどバグを発見しやすく、機能を付け加えるのも簡単です。
コードを読みやすく保つには
この「読みやすさ」を実現するにはどうすればいいでしょうか? すぐ思い付くのは「シンプルにする」ということです。シンプルでないコードは読めません。たとえば、次のようなコードは読めません。
- ひとつのメソッドが数十行に及ぶほど長い
- 多重の if などの複雑な制御構造を使っている
したがって、コードベースを健全に保つには読みづらい部分を改善し続ける必要があります。
ここでいくつか問題があります。
- 読みやすさというのはその日の気分や体調に影響されやすく、個人間でのばらつきも大きいものです。そこで、読みやすさを示す客観的な指標が必要です。
- コード全体に目を通し読みにくい箇所をみつけるのはとても大変です。そこで、読みやすさの指標に基いて問題のある部分を発見してくれるツールが必須です。
flog ターゲット
Trema では「読みにくいコード」を機械的に検出する仕組みを提供しています。 flog という rake ターゲットは、ソースコード中の複雑なメソッドを flog 独自の指標を元に検出します。複雑度が高いメソッドほど点数が大きくなります。コマンド一発なので、コード全体にくまなく目を通す必要もなく一瞬で検出できます。
実行してみましょう。
$ rake flog 10.2: Trema::Process#initialize 10.3: Trema::Ofctl#flows 10.5: Trema#tmp 10.7: Trema::Link#real_eth 10.8: Trema::SwitchDaemon#options 10.9: Trema::DSL::Syntax#rswitch 10.9: Trema::DSL::Syntax#switch 10.9: Trema::DSL::Syntax#run 11.2: Trema::Daemon#daemon_id 11.4: Trema::Controller#daemonize! 11.6: Trema::DSL::Syntax#vhost 11.6: Trema#file 11.7: Trema::DSL::Parser#eval 11.7: Trema::DSL::Parser#parse 11.7: Trema::Command#dump 11.7: Trema::Command#trema_up 11.8: Trema::Ofctl#users_flows 11.9: SubProcess::Shell#handle_child_output 12.2: Trema::DSL::Configuration#initialize 12.3: Trema::Ofctl#add_flow 12.3: Trema::Executables#path 12.4: Trema::DSL::Syntax#vswitch 12.5: Trema::VendorAction#initialize 12.5: Trema::Phost#none 12.8: Trema::Shell#vhost 13.0: Trema::App#none 13.2: Trema::Command#shell 13.9: MonkeyPatch::Module::ClassMethod#define_class_method 14.1: SubProcess::Shell#exec 14.2: Trema::SendOutPort#initialize 14.4: Trema::Process#kill! 14.4: Trema::Switch::inherited 15.2: Trema::Shell#reset_stats 15.2: Trema::Daemon#log_file 16.2: Trema::IP#prefixlen 16.4: Trema::DSL::Runner#maybe_run_switches 16.4: Trema::Daemon#wait_until_up 16.6: Trema::StatsHelper#to_s 16.7: Trema::Daemon::included 17.7: Trema::Link#initialize 17.8: SubProcess::Process#initialize 18.2: Trema::Cli#send_packets 19.2: Trema::Controller#none 19.8: Trema::App#initialize 21.0: Trema::Executables#none 21.1: Trema::Enqueue#initialize 21.4: Trema::Daemon#run! 22.5: Trema::Shell#show_stats 23.3: Trema::Shell#vswitch 23.6: Trema::Daemon#pid_file 26.7: Trema::DSL::Runner#maybe_run_apps 27.4: Trema::Command#none 27.9: Trema::Flow::parse 30.6: Trema::Shell#link 30.8: Trema::Command#trema_run 38.1: Trema::Shell#run 40.5: Trema::Util#cleanup 40.8: Trema#none 45.7: Trema::Command#load_config 56.1: Trema::Cli#send_packets_options 59.8: Timers::TimerMethods#none 62.7: Trema::DSL::Parser#configure 66.0: Trema::Command#trema_send_packets 82.9: Trema::DSL::Runner#maybe_run_switch_manager rake aborted! 64 methods have a flog complexity > 10
64 個のメソッドがひっかかってしまいました。Trema の flog の設定では複雑度が 10 以上のメソッドを検出します。この 10 という値はかなり厳しめなので多めのメソッドがひっかかってしまったようです。
複雑度を下げるには
メソッドの複雑度を下げるにはリファクタリングが有効です。大抵は、リファクタリングの中でもとくにオーソドックスなメソッドの抽出だけでメソッドを単純化できます。
もし、複雑なメソッドを単純化するパッチを作った人は Pull-Request をぜひおねがいします。こうしたリファクタリングのパッチは、機能的な面の変更が無いのでどこか出しづらい雰囲気があります。しかし、コードベースを改善するという意味ではとても貢献度の高いパッチです。