リーダブルコード読書メモ


プログラマーに大人気のリーダブルコードを読みました。
最初にこの本を読んだ時はこの本の凄さがあまり分かりませんでしたが、一度webサービスを作った後に再度読んでみるとわかりみがしゅごい。
読んでいて重要だと思った部分を中心にまとめます!

9章変数と読みやすさ

■変数を削除する
中間結果を保持するためのだけの変数は、関数から早く返す事で変数を削除できないか検討する。

■制御フロー変数を削除する
以下のようにプログラムの実行を制御するためだけの変数を使う事があるが、このような制御フロー変数は、continueをbreakに変えるなどして削除できる。

boolean done = false;
while( xxx && !done) {
    if (...) {
     done = true;
     coninue;
    }
}

■変数のスコープを縮める
グローバル変数に限らず、メンバ変数など、全ての変数のスコープを縮めるのは良い考えだ。
そうすることで一度に考えなければいけない変数を減らせる。
メンバ変数はクラスの中でミニグローバル変数となっているため、なるべく使う数を減らしたほうがいい。
例えばメンバ変数を使っているメソッドが2つしか無いようなケースでは、メンバ変数を無くしてメソッド内にローカル変数を定義したほうがよいかもしれない。

■JavaScriptで「プライベート」変数を作る
グローバル変数はコードを読む人を不安にさせる。この問題を回避するには、変数をクロージャで包んであげればよい。

var submit_form = (function () {
    var submitted = false; // クロージャに包む変数

    return function (form_name) {
        if (submitted) {
            return;
        }
        ....
        submitted = true;
     };
}());

■JavaScriptのグローバルスコープ
JavaScriptでは、変数の定義にvarをつけないと、その変数はグローバルスコープに入ってしまう。
ベストプラクティスは、変数を定義するときは常にvarキーワードをつけることだ。

■PythonとJavaScriptのネストしないスコープ
C++やJavaでは、if,for,tryなどのブロックで定義された変数は、スコープがそのブロックに制限される。
しかし、PythonやJavaScriptでは、ブロックで定義された変数はその関数全体に「こぼれ出る」。
そのため、ブロック内で変数を定義すると、後から変数を探す時に分かり辛くなってしまうし、初期化が漏れてバグが発生するおそれがある。
これを回避するには、ブロックの前で変数を定義するか、変数を使わないように削除する方法がある。

10章 無関係の下位問題を抽出する

無関係の下位問題を積極的い見つけて抽出するとよい
1.関数やコードブロックを見て「このコードの高レベルの目標は何か?」と地紋する
2.コードの各行に対して、「高レベルの目標に直接的に効果があるのか? あるいは、無関係の下位問題を解決しているのか?」と自問する。
3.無関係の下位問題を解決しているコードが相当量あれば、それらを抽出して別の関数にする

■入門的な例: findClosestLocation()
与えられた地点から最も近い場所を見つけるコードがあった場合、2つの地点の球面距離を算出するコードは下位問題を取り扱っているため、新しい関数を作るとよい。

■純粋なユーティリティコード
文字列操作やファイルの読み書きなど、プログラムの基本的なタスクはライブラリとして用意されているが、たまに自分で用意しなければいけない事がある。
例えばC++ではファイルの中身をすべて読み込む方法が用意されていないため、自分でコードを書くことになる。
これは「無関係の下位問題」の典型的な例となる。

11章 1度に1つのことを

一度に複数のことをするコードは理解しにくい。例えば、オブジェクトを作成して、データを綺麗にして、入力をパースするなど。
これらのコードが全て絡み合っていると、タスクが個別に完結しているコードよりも理解するのが難しい。
「1度に1つのタスクをするため」の手順
1.コードが行っているタスクを全て列挙する
2.タスクをできるだけ異なる関数に分割する。少なくとも異なる領域に分割する。

■タスクは小さくできる
ブログに設置する投票用のウィジェットがあるとする。ユーザはアップ(賛成)かダウン(反対)で投票できる。
scoreは全ての投票を合計したものである。アップは1点、ダウンは-1点となる。
このような処理を行う関数を作る場合、2つのタスクに分られる。
1. 押されたボタンを数値に「パース」する
2. scoreを更新する
タスクごとに関数を作成すると理解しやすくなる。

■オブジェクトから値を抽出する
ユーザの所在地を読みやすい文字列に整形するJavaScriptのコードを想定する。
例えば、「Santa Monica,USA」や「Paris, France」のような感じだ。
処理フローは以下のようになる
・都市を選ぶときは「市や町」、「都市や郡」、「州や地域」の順番で使用可能なものを使う。
・以上の3つ全てが使えない場合は、「都市」い「Middle-of-Nowhere」というデフォルト値を表示する。
・国に「国名」が使えない場合は、「Planet Earth」というデフォルト値を表示する
この処理を行うコードを書いた時、タスクは以下のようになる。
1.location_infoディクショナリから値を抽出
2.「都市」の優先順位を調べる。何も見つからなかったら、デフォルトで「Middle-of-Nowhere」にする
3.「国」を取得する。なければ「Planet Earth」にする
4.placeを更新する

12章 コードに思いを込める

誰かに複雑な考えを伝える時は、細かいことまで話そうとすると相手を混乱させてしまう。
自分よりも知識が少ない人が理解できるような「簡単な言葉」で説明する能力が大切だ。
1.コードの動作を簡単な言葉で同僚にもわかるように説明する。
2.その説明のなかで使っているキーワードやフレーズに注目する
3.その説明に合わせてコードを書く

■ロジックを明確に説明する
PHPコードで、ユーザにページを閲覧する権限があるかどうかを確認して、もし権限がなければ、権限がないことをユーザに知らせるページに戻す。
このコードの実装を以下のように簡単な言葉でロジックを説明してみる。

権限があるのは、以下の2つ
1) 管理者
2) 文書の所有者(文書がある場合)
その他は、権限がない

このようにロジックを整理してからコードを書くと、理解しやすいコードになる。

13章 短いコードを書く

プログラマが学ぶべき最も大切な技能は、コードを書かないことを知ることなのかもしれない。
ライブラリの再利用や機能の削除をすることで、時間を節約したり、コードを完結に維持したりできる。

■夜質問と要求の分割
・店舗検索システム
「店舗検索システム」を作っているとしよう。要求は以下のようなものだ。
任意のユーザの緯度経度に対して最も近い店舗を検索する。
これを100%正しく実装するには、日付変更線をまたいでいる時の処理や、1マイルあたりの経度に対応した地球の曲率の調整をする必要がある。
しかし、アプリケーションがテキサス州にある30軒の店を探すだけなら、上記の実装は考える必要はない。

■コードを小さく保つ
プロジェクトが進んできてファイルが増えてくると、どの関数がどの関数を呼び出しているのか分からなくなり、新しく機能を追加するのが苦痛になってくる。
プロジェクトが成長しても、以下のことをしてコードをできるだけ軽量に維持する必要がある。
・汎用的なユーティリティコードを作成し、コードの重複を削除する
・未使用のコードや無用の機能を削除する

■身近なライブラリに親しむ
プログラマは既存のライブラリで問題を解決できることを知らないことが多い。
たまには標準ライブラリの全ての関数、モジュール・型の名前を15分かけて読んでみよう。

14章 テストと読みやすさ

■テストを読みやすくて保守しやすいものにする
他のプログラマが安心してテストの追加や変更ができるように、テストコードを読みやすくする。
テストコードが大きくて恐ろしいものだと、本物のコードを修正するのを恐れたり、新しいコードを書いた時にテストを追加しなくなってしまう。

■テストを読みやすくする
検索結果のスコアをソートしてフィルタする関数のテストコードを想定する。
大切ではない詳細はユーザから隠し、大切な詳細は目立つようにする。
テストの本質ではない変数やURLは、テストの内容を高レベルから示したものではない。

・最小のテストを作る
「12章コードに思いを込める」でやったように、このテストが何をしようとしているのか以下のような簡単な言葉で説明する。

文書のスコアは[-5, 1, 4, -99998.7, 3]である。
SortAndFilterDocs()を呼び出したあとのスコアは[4, 3, 1]である。
スコアはこの順番でなければいけない。

このようなテストをする場合、テストコードは以下のようになるといいだろう。

CheckScoresBeforeAfter("-5, 1, 4, -99998.7, 3", "4, 3, 1");

CheckScoresBeforeAfterはヘルパー関数として作成する。

■テストの適切な入力値を選択する
適切な入力値はコードを完全にテストし、簡単に読めるものでなければいけない。

・入力値を単純化する
-99998.7のような大きなマイナス値を示したい場合は、-1e100のような簡潔な値にしたほうがよい。

■1つの機能に複数のテスト
入力値を1つ作るのではなく、空行入力のテスト、マイナス値のテスト、重複のテストなど、小さなテストを複数作るほうがよい。