TL;DR
- 明白なコードとは、読み手があまり考えずに読めて、最初の推測が正しいコードである。
- 明白かどうかはコードレビューで他人に判断してもらうのが最良で、良い名前、一貫性、空白、コメントで明白にできる。
内容
明白なコードとは
コードが明白であるとは、あまり考えずに読めて、コードの動作や意味についての最初の推測が正しいことである。明白なコードは、不明瞭なコードより必要なコメントが少ない。逆に、明白でないコードは、必要な情報を集めるのに多くの時間と労力がかかり、作業効率が下がり、誤解やバグを招く。これはレッドフラグの 1 つである。
コードが明白かどうかを判断する最良の方法は、コードレビューである。自分のコードを読んだ誰かが分かりにくいと言ったら、自分には明白に見えても、そのコードは明白ではない。
コードを明白にする技法
- 良い名前を選ぶ(第14章 Choosing Names): コードの動作を明確にし、コメントの必要性を減らす
- 一貫性を保つ(第17章 Consistency): 似たことを似た方法で行えば、読み手がパターンを認識できる
- 空白を適切に使う: 関数の引数の説明を引数ごとに改行する、メソッド内のコードブロックを空行で区切る、文の構造を明確にする
- コメントを書く: 不足している情報を補う(第12章 Why Write Comments? The Four Excuses)。ただし、何が読み手を混乱させるか、どんな情報がその混乱を解消するかを、読み手の立場で考える必要がある
コードを不明瞭にするもの
イベント駆動型プログラミング
イベント駆動型プログラミングでは、マウスボタンの押下やパケットの到着などのイベントが起きると、あるモジュールが他の部分に通知し、あらかじめ登録された関数が呼ばれる。イベントハンドラは直接呼ばれず、イベントモジュールが関数ポインタやインタフェースを通じて間接的に呼ぶので、制御の流れを追いにくい。対策として、ハンドラのインタフェースのコメントに、いつ呼ばれるかを書く。
/**
* This method is invoked in the dispatch thread by a transport if a
* transport-level error prevents an RPC from completing.
*/
void Transport::RpcNotifier::failed() {
...
}汎用的なコンテナ
汎用的なコンテナとは、Java の Pair や C++ の std::pair のように、2 つ以上の要素を 1 つのオブジェクトにまとめるクラスである。
return new Pair<Integer, Boolean>(currentTerm, false);呼び出し側は result.getKey() や result.getValue() で値を取り出すが、それぞれの値が何を意味するのか分からない。対策として、汎用的なコンテナを使わず、用途に特化したクラスを定義して要素に意味のある名前を付ける。コメントで情報を補うこともできる。
宣言と実際の型が異なるコード
private List<Message> incomingMessageList;
...
incomingMessageList = new ArrayList<Message>();List は ArrayList の上位の型なのでコンパイルは通る。しかし、ArrayList は List の他の実装と性能やスレッドセーフの特性が異なるので、読み手を誤解させうる。宣言と実際の型は一致させる。
読み手の期待に反するコード
通常、メインプログラムが終わるとアプリケーションも終了する。しかし次のコードでは、RaftClient のコンストラクタが追加のスレッドを作るので、メインスレッドが終わってもアプリケーションは動き続ける。
public static void main(String[] args) {
...
new RaftClient(myAddress, serverAddresses);
}このように読み手が期待しない振る舞いをする場合は(驚き最小の原則)、RaftClient のコンストラクタのインタフェースか、呼び出しの直前に、別のスレッドで動き続けることをコメントで書く。
まとめ
コードが明白であるとは、読み手が常にそのコードに関する重要な情報を持っていることである。コードを明白にする方法は 3 つある。抽象化や特殊なケースの排除などの設計の技法で、必要な情報を減らす。慣習に従い、期待どおりに振る舞うことで、読み手がすでに知っている情報を活かす。良い名前やコメントで、必要な情報をコードの中に示す。