「リーダブルコード」を読了してからしばらくするので、振り返りのために、当時読みながら書いていた読書メモを元に軽く内容をブログに残しておこうと思います。
ご存知の人も多いでしょうが、「リーダブルコード」は、読みやすいコードを記述し、効率的に開発を進めるための、コーディング手法や考え方・意識がわかりやすくまとめられている書籍です。 ざっと読んだ感じ、ちょうど私と同じような、ある程度開発経験はあるが、まだまだ経験が浅い若手プログラマ(半年〜2年目)くらいの方には特にジャストフィットしそうな内容・レベル。明日からの開発にすぐさま活かせます。
以下が読書メモ
前提となる考え
コードは他の人が最短時間で理解できるようにかかなければならない
表面上の改善
命名規則やコメントなどの表面的に改善できる部分
名前に情報を詰め込む
- 明確な単語を選ぶ 役割がすぐにわかるような単語
- tmpやretvalのような汎用的な名前を避ける 汎用的な名前を使用する場面は、あくまでも一時的な値の保管が大切で、変数の名前には特に意味がない時 汎用的な名前を使う場合はそれ相応の理由が必要(基本使用しないスタンス)
- 具体的な名前をつける 例えば、CanListenOnPort()など
- 名前に情報を追加する つけておいた方がバグを減らせるような重要な属性などを名前に詰め込む
- スコープの大きな変数には長い名前をつける 逆にスコープの短い変数には必要以上に長い名前をつけない方が良い
- 大文字やアンダーコアなどに意味を含める
誤解されない名前
名前が「他の意味と間違えられることはないだろうか?」と何度も自問自答する
-
パターンごとに命名を固定
- 限界値を含める時はminとmaxを使う
- 範囲を指定するときはfirstとlastを使う
- 包含/排他的範囲にはbeganとendを使う
- ブール値の名前には頭にis, has, can, shouldなどをつける
-
ユーザの期待にあわせる
たとえば、getで始まるメソッドは軽量アクセッサであると期待されるので、getを使う場合は、処理を軽くする、それ以外の場合は命名方法を変える
美しさ
一貫性のあるスタイルは「正しい」スタイルよりも大切である
-
一貫性のある簡潔な改行位置
これはIDEエディタを使用している場合、ショートカットで一発解決
-
縦の線をまっすぐにする
配列や文字列などカンマで縦に揃えて並べるなど
-
一貫性と意味のある並び
-
宣言をブロックにまとめる
役割などのグループごとにわける
コメントすべきことを知る
コメントの目的は、書き手の意図を読み手に知らせることである。 基本は、優れたコード>ひどいコード+優れたコメントなので、コードの読みにくさを補うコメントが必要になるときはない。 ひどい名前が存在する場合、コメントをつけずに名前自体を変更する。
コメントすべきではないこと
- コードからすぐにわかること
- コメントのためのコメント
コメントすべきこと
-
自分の考えの記録
見た人が助かるようなコードに対する大切な考えを記録する。下手に最適化しようとするのを防いだり、コードが汚い理由を書いて誰かに助けを求めたり。
例えば、
//左右の比較よりもハッシュの計算コストの方が高い
//サブクラス’ResourceNode’を作って整理した方がいいかもしれない など
-
コードの欠陥
//TODO: もっと高速なアルゴリズムを使う
TODO以外にもFIXEME, HACK, XXXなどが使える
-
定数
定数にまつわる背景を記述する
読み手の立場によって考える
-
質問されそうなことを想像する
-
ハマりそうなワナを告知する
このコードを見てひっくりすることは何だろう?どんなふうに間違えて使う可能性があるだろう?
-
「全体像」のコメント
クラスの連携や、データはどのようにシステムを流れているのか、エントリーポイントはどこにあるのかなど。
-
要約コメント
コメントは正確で簡潔に
コメントは情報に対する情報の比率が高くなければならない
-
コメントを簡潔にしておく
//intはCategoryType
//pairの最初のfloatは’scope’
//二つ目は’weight’
→//CategoryType -> (score, weight)
-
あいまいな代名詞は避ける
-
関数の動作を正確に記述する
//このファイルに含まれる行数を返す
→//このファイルに含まれる改行文字(‘¥n’)を数える
-
入出力のコーナーケースに実例を使う
-
コードの位置を書く
動作内容をつらつらと記述するのではない
-
よくわからない引数には「名前付き引数」コメントをつける
-
情報密度の高い言葉を使う
キャッシュ層や正規化するなど
ループとロジックの単純化
表面上ではなく、もっと深い部分であるロジック部分について。プログラムの構造のちょっとした変更技法。
制御フローは読みやすくする
-
条件やループなどの制御フローはできるだけ「自然」にする
コードの読み手が立ち止まったり読み返したりしないように書く
- 条件式の引数の並び順(変化する値を左にする)
- if/elseブロックの並び順(条件は否定形よりも肯定形を使う/ 単純な条件を先に書く/ 関心を引く条件や目立つ条件を先に書く)
- do/whileループを避ける
- 関数から早く返す
- ネストを浅くする
-
行数を短くするよりも、他の人が理解するのにかかる時間を短くする
例えば、基本的にはif/elseを使う。三項演算子はそれによって簡潔になるときにだけ使う。
-
変更するときにはコードを新鮮な目で見る。一歩下がって全体を見る
→ネストが深くなる仕組みとして、変更の際、最も簡単にコードを挿入できる部分に目が行きそのまま挿入・変更する。結果的にコード自体は間違っていないがネストが深くなる。
→解決策:
早めに返してネストを削除する。ループが一つだと、continueを使えば、ループ内部のネストを削除できる。
巨大な式を分割する
- 巨大な式は飲み込みやすい大きさに分割する
→説明変数、要約変数、ド・モルガンの法則を使用する
-
「頭がいい」コードに気をつける
複雑なロジックよりも単純で理解しやすいコーディングを意識する
変数と読みやすさ
-
邪魔な変数を削除する
→役に立たない一時変数(複雑な式を分割していない、より明確になっていない、一度しか使っていないので重複コードの削除になっていない)、 中間結果を削除する
-
変数のスコープをできるだけ小さくする
変数のことが見えるコードの行数をできるだけ減らす
メソッドをできるだけstaticにしたり、大きなクラスを小さなクラスに分割する(クラスで相互にメンバを参照しあったら意味がない、分割したいのはデータ)
定義の位置を下げる(メソッドの先頭にまとめて変数を書くのでなく、変数が登場する流れに従って書く)
-
一度だけ書き込む変数を使う
あるいは定数などのイミュータブルにする方法をとるとコードがわかりやすくなる
変数を操作する場所が増えると、現在位置が難しくなる
コードの再編成
ロジック部分について。コードを大きく変更する技法。
無関係の下位問題を抽出する
- 関数やコードブロックを見て「このコードの高レベルの目標は何か?」と自問する
- コードの各行に対して「高レベルの目標に直接的に効果があるのか?あるいは、無関係の下位問題を解決しているのか?」と自問する
- 無関係の下位問題を解決しているコードが相当量あれば、それらを抽出して別の関数にする
下位問題を抽出するとそれぞれが自己完結し、読みやすさやスケーラビリティが上がる
-
汎用コードをたくさん作る
-
プロジェクトに特化した機能
抽出する下位問題はプロジェクトからは独立している方がよいが、別に必ずしもそうでなくてもよく、プロジェクトに特化した機能であってもよい。
-
既存のインターフェースを簡潔にする
自分でラッパー関数を用いて汚いインターフェースを覆い、コードを簡潔にする。
-
やりすぎに注意
小さい関数を用意しすぎても読みづらくなってしまう
一度に一つのことを
-
コードは一つずつタスクを行うようにしなければいけない
タスクは小さくできる
読みにくい部分があれば、そこには別の関数やクラスに分割できるタスクもあるかもしれない
コードに思いを込める
- コードの動作を簡単な言葉で同僚にも分かるように説明する
- その説明の中で使っているキーワードやフレーズに注目する
- その説明に合わせてコードを書く
- ロジックを明確に説明する
- ライブラリを知る
短いコードを書く
できるだけコードを書かないようにする
- 不必要な機能をプロダクトから削除する。過剰な機能は持たせない
- 最も簡単に問題を解決できるような要求を与える
- 定期的にすべてのAPIを読んで、標準ライブラリに慣れ親しんでおく
その他テーマ
テストと読みやすさ
テストが読みやすければ、テストが書きやすくなり、みんながテストを追加しやすくなる。また、本物のコードをテストしやすく設計すれば、コードの設計が全体的に改善できる。
- テストのトップレベルはできるだけ簡潔にする。入出力のテストはコード1行で記述できるといい
- テストが失敗したらバグの発見や修正がしやすいようなエラーメッセージを表示する
- コードを完全にテストする最も簡単な入出力の組み合わせを選択する
- テスト関数に説明的な名前をつけて、何をテストしようとしているのかを明らかにする。Test1()ではなく、Test<関数名><状況>のような名前にする
まとめ
- 読み手の立場にたって考える!
- 単純かつ簡潔なコーディング!
- 下位問題の抽出で自己完結部分を多くする!