リーダブルコード備忘録


概要

リーダブルコードを読んだ内容を個人で確認するためのメモです。(記載途中)

リーダブルコードの内容

自分の書いたコードを他の人が見ても理解しやすく読みやすくするためのノウハウがまとめられている

表面上の改善

読みやすいコードにするには、表面上の改善を行うだけでも効果あり。
例)適切な名前をつける、優れたコメントを書く、コードを綺麗にフォーマットするなど。

名前に情報を詰め込む

プログラムで使用する名前は短いコメントのようなもので、良い名前はそれだけで
多くの情報を伝える事ができる

明確な単語を選ぶ

名前に情報を詰め込むには、明確な単語を選ぶ必要がある。

例)「GetPage」という関数の場合、どこからページを取ってくるか不明確。
仮にインターネットから取ってくるのであればFetchPageやDownloadPageの方が明確。

汎用的な名前は避ける

例)関数の戻り値を「retval」とした場合、どんな値を返しているか不明確。
仮に値を2乗にして返す関数の戻り値ならsum_squaresの方が良い。
※アドバイス:retval,it,tmpのような汎用的な名前を使うときは、それ相応の理由を用意する。

名前に情報を追加する
  • 変数名に単位を追加する

    • delay → delay_secs
    • size → size_mb
    • limit → max_kbps
    • angle → degrees_cw
  • 変数名に重要な属性を追加する

    • password → plaintext_password
    • comment → unescaped_comment
    • html → html_utf8
    • data → data_urlenc

誤解されない名前

英単語はプログラミングに使うには曖昧なものが多い。
名前をつける時には誤解されないかどうかを考慮すると良い。

例)filter()という関数があった場合、引数の値を選択するのか除外するのか判断がつかない。
選択する場合はselect、除外する場合はexcludeにした方が良い。

コメントするべき事を知る

コメントに残すべきではないこと

  • コードから内容が読み取れること
  • 読み辛い、無駄なロジックのあるコードを補うこと(コメントを書くのではくコードを修正する)

記録すべき自分の考え:

  • なぜコードが他のやり方ではなくこうなっているのか。
  • コードの問題点をTODO:やXXX:などを使って示す。
    • TODO: あとで手をつける
    • FIXME: 既知の不具合があるコード
    • HACK: あまりキレイじゃない解決策
    • XXX: 危険!大きな問題がある

読み手の立場になって考える:

  • 読み手が驚くような動作は文書化しておく。
  • ファイルやクラスには「全体像」のコメントを書く。
  • 読み手が細部に捕らわれないように、コードブロックにコメントをつけて概要をまとめる

ループとロジックの単純化

制御フローを読みやすくする

  • 比較を書く時には、変化する値を左に、より安定した値を右に配置する
    • 例) bytes_received < bytes_expected
  • if/else文は一般的には、肯定系・単純・目立つものを先に処理する
  • 三項演算子(? :)は基本的にif/else文に置き換えた方が良い。
    • 三項演算子を使用する事で簡潔になるときだけ使う。
  • do/whileループ,gotoなどはコードが読みにくくなることが多い。 代替となるものがあるので、これらは使わない方が良い。
  • 深いネストは避ける。(早めにreturnしたり、continueを活用する)

巨大な式を分割する

  • 説明変数、要約変数(式を代入した変数)を使用して式を分割する
  • ド・モルガンの法則を利用する
    • not (a or b or c) ⇔ (not a) and (not b) and (not c)
    • not (a and b and c) ⇔ (not a) or (not b) or (not c)

邪魔な変数は削除する

不要な一時変数の削除

以下の特徴をもつ変数は削除した方が見やすい

  • 複雑な式を削除していない。
  • より明確になっていない。
    • 例)now = datetime.now()
  • 一度しか使われず、重複コードを削除していない。
  • 中間結果を保持するためだけの変数 ※1

※1 中間結果を保持するためだけの変数の例

// サンプルコード(修正前)
function (array, value_to_remove) {
    var index_to_remove
    for (var i = 0; i < value_to_remove) {
        if (array[i] === value_to_remove) {
            index_to_remove = i; // 中間結果を保持するためだけの変数
            break;
        }
        if (index_to_remove !== null) {
            array.splice(index_to_remove, 1)
        }
    }
}

※1 中間結果を保持する変数(index_to_remove)を削除して結果をそのまま使用する。

// サンプルコード(修正後)
function (array, value_to_remove) {
    for (var i = 0; i < value_to_remove) {
        if (array[i] === value_to_remove) {
            array.splice(i, 1);
            return;
        }
    }
}

変数のスコープを縮める

変数が見えるコード行をできるだけ減らす(グローバル変数を避けるなど)
  • ミニグローバルな変数はローカル変数に格下げする

    • 例)巨大なクラスの中で二つのメソッドでしか使われていないようなメンバ変数
  • javascriptでプライベート変数を作る

  • 変数定義時にvarを使用する(変数のスコープを制限する)

一度だけ書き込む変数を使う。

変数に値を設定するのを一度だけにすれば(もしくは、constやfinalなどを使用)
コードが理解しやすくなる。

コードの再構成

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

プロジェクト固有のコードから汎用コードを分離することで、堅牢で読みやすいコードに改善できる。

実例として下記のメッソドを修正します。

 // 与えられた経度緯度に最も近い'array'の要素を返す。
 // 地球が完全な球体であることを前提としている。
 var findClosestLocation = function (lat, lng, array) {
    var closest;
    var closest_dist = Number.MAX_VALUE;
    for (var i = 0; i < array.lnegth; i += 1) {
        // 2つの地点をラジアンに変関する
        var lat_rad = radians(lat);
        var lng_rad = radians(lng);
        var lat2_rad = radians(array[i].latitude);
        var lng2_rad = radians(array[i].longitude);

        //「球面三角法の第二余弦定理」の公式を使う。
        var dist = Math.acos(Math.sin(lat_rad) * Math.sin(lat2_rad)) +
                            Math.cos(lat_rad) * Math.cos(lat2_rad) *
                            Math.cos(lng2_rad - lng_rad));
        if (dist < closest_dist) {
            closest = array[i];
            closest_dist = dist;
        }
    }
    return closest;
 }

findClosestLocationは「与えられた地点から最も近い場所を見つける」メソッドです。
ループ内で「2つの地点(緯度、経度)の球面距離」を算出していますが、このメッソドでは
無関係な下位問題となリマス
コード量も多いので新しい関数に分けると良いです。

var spherical_distance = function (lat1, lng1, lat2, lng2) {
    // 2つの地点をラジアンに変関する
    var lat1_rad = radians(lat1);
    var lng1_rad = radians(lng1);
    var lat2_rad = radians(lat2);
    var lng2_rad = radians(lng2);

    //「球面三角法の第二余弦定理」の公式を使う。
    return Math.acos(Math.sin(lat1_rad) * Math.sin(lat2_rad) +
                     Math.cos(lat1_rad) * Math.cos(lat2_rad) *
                     Math.cos(lng2_rad - lng1_rad));
};

残ったコードは以下のようなります。

 var findClosestLocation = function (lat, lng, array) {
    var closest;
    var closest_dist = Number.MAX_VALUE;
    for (var i = 0; i < array.lnegth; i += 1) {
        //「球面三角法の第二余弦定理」の公式を使う。
        var dist = spherical_distance(lat, lng, array[i].latitude, array[i].longitube);
        if (dist < closest_dist) {
            closest = array[i];
            closest_dist = dist;
        }
    }
    return closest;
 };