良さげなソースコードを記述するための3つの簡単な事

お仕事を始めて、だいぶ多くの人のソースコードを見るようになりましたが、首をかしげたくなるコードに出会うことが多かったです。
そこで、主観的にココさえ守ればせめて読みやすい、といった点を書き出してみることにします。
とりあえず公開しますが、殴り書きなので、ちょくちょく修正入れるかもしれません。


一応、言語としてはJavaを想定してますが、C#/VB.NETなどでも同じことがいえると思います。

概要

たった3つ守ってください。
1. ネストを浅くする
2. 意味を与える(べた書きせず、適切なメソッドに処理を切り分ける)
3. 名前に気を使う


何を当たり前だ、と思ったあなた。
現代的プログラマとしては正常だと思います。
一応、説明を以下に。

1. ネストを浅くする

ここでは、ネストとはソースのブロックの中にブロックを書くことを意味しています。
例えば、ifの中にifを書いたりすると、どんどんネストは深くなっていきます。


フローチャートを意識して設計書を起こす場合、ソースコードのネストが深くなることが多い傾向があると思います。
と、いうか、フローチャートそのものが処理を合流させるという考えがある*1ため、それをベースにした設計書までわけのわからん手順が記述されることになってるように思います。
さっさとフローチャートベースで考えさせるのをやめさせればいいのに。


プログラムを書くにしても、設計書やフローチャートを書くにしても、まずは、その処理の正常フローを考え、そのフローを極力浅いネストで書きましょう。
また、処理を書いていてネストが深くなりすぎた場合、1度しか使われなくてもとりあえずメソッドに切り出してしまいましょう。
怪獣コストガーはもはや通用しません。怪獣メンドウダーはIDEの使い方を覚えてください。多分、ここで想定している言語では、優秀なIDEがあるはずです。


正常フローを割り出したら、そこから例外処理を切り出しましょう。
そここそ、ガードでネストさせる「処理の特殊な部分」です。
次のコードテンプレートを例にとって考えます。

int hoge(String str) {
  if (strが〜だったら) {
    throw new RuntimeException();
  } else {
    // hogeの処理を行う。
  }
}


このhogeという関数では、本来はelse句の中の処理を行いたいはずですが、elseに書かれているせいでその意図があいまいになってしまっています。
ここでは{}括弧のネストが1段だから、まだわかりやすいですが、これを何段階も続けられると途端にスパゲッティと化します。


「処理のネストを浅くする」ために、まずはhogeの処理をhogeの最も浅いネストに書き出します。

int hoge(String str) {
  // hogeの処理
}

本当ならば、hogeはこれだけで終わらせたいところです。
しかし、この処理はstrが特定条件にあるときだけは「例外的にできません」。
その考えで記述を行うと次のようになります。

int hoge(String str) {
  if (strが〜だったら) {
    throw new RuntimeException();
  }
  // この時点でhogeはstrを処理できる事が保障できる。
  // hogeの処理
}


コードは上から下に実行されます。
同様に、データも上から下に流れていきます。
どんどん処理できる範囲を小さくしぼって行きましょう。


最初から特定のケースを考えすぎる必要はないです。
処理をする、大きな流れを常に頭に置いておきましょう。
いまやってる処理が支流である場合、処理終了後に本流に戻れるかを考えてみてください。
それを実践してると、自然に小さい責任の単位で分割なんてできるようになってます。


return1個というルールを破り捨てましょう。
これ以上処理ができないなら、さっさとそのメンドクサイ処理を考えないようにしましょう。
何にも難しいことはありません。
むしろ、メンドクサイ処理を後々まで引きずって考える必要が無い分、ほとんどの場合簡単になります。


追記: id:rf0444いわく、こういうのはIDEコンパイルエラーにしてしまえばいいとのこと。
不要なelse節をコンパイルエラーにするように初期プロジェクト設定しとけば、これ大体防げるんじゃないですかね。

2. 意味を与える

処理をだらだら書いても、何がしたいのかがすぐには分かりません。
例えば、Beanに逐一代入する処理だけを延々と50行ぐらい書かれても、それは目的を達成する手段の1実装であり、ここでの目的は「Beanに値を設定する」という言葉で十分説明できます。


意味が分かりづらいと思ったら、変数やメソッドに分けましょう。
この時、変数やメソッドに適切な名前を付けましょう。
また、メソッドであれば、ドキュメンテーションコメントに好きなだけ目的を文字にして書きましょう。


本文中のコメント(//など)で意味を与える方法でも良いかもしれませんが、
どこから始まってどこで終わるのかが明確ではない場合もあるため、オススメはしません。
1度だけなら誤射かもしれませんが、これを繰り返していると、1メソッドが1000行とかいってしまうソースコードをあっという間に生成してしまいます。
「どんな事をしているのか」という問いに答えるソースコードや仕様書を書きましょう。
「●●をして××をして○○をします」と言われるよりも、「初期化して、株価を計算します」と概要を言った方が分かりよいはずです。


「株価はどうやって計算しますか?」という疑問になって初めて、その詳細を展開すればいいのです。
プログラムでは、メソッド呼び出しとメソッドの実装という形で実現できますし、仕様書では書き方を工夫する事で自由に実現できるはずです。


フォーマット? 意図を伝えさせない仕様書なんかただのゴミ屑です。意図を書けない仕様書のフォーマットとかrm -rfしてください。

3. 名前に気を使う

何をするかが分かる名前を使いましょう。
関数に、変数に、大いに利用しましょう。
コメントなんて書く前に、関数・変数名で内容が分かってしまうことを目指しましょう。
(ドキュメンテーションコメントを除く)ソースコード中のコメントは最後の手段みたいなものと覚悟するといいと思います。
特別な意図を記述するときには大いに役立つと思いますが、コメントが無くても読めるソースコードを目指しましょう。


また、使用される範囲が極小であれば、影響範囲が大きくないので、名前に気を使う必要性が薄くなります。
String sとか、StringBuilder bufとか、メソッドを小さく、短くして、どんどん使っていいと思います。
ある意味では、名前に気を使わなくなるほど問題を分割し、意味を与えることが、本当に名前に気を使う事になるのかもしれません。

*1:これは私個人の推測です