LOGv:20171214

リーダブルコードまとめ

リーダブルコードというプログラミングコードの品質管理や情報共有におけるヒントを記載した本が良書だと聞いていたので盆休みに読んでみた。以下備忘録まとめ

はじめに

この本は4部構成になっており

  • 表面上の改善 (変数や関数などの名前の付け方、コメントの書き方)
  • ループとロジックの単純化
  • コードの再構成 (タスクの分割、汎用化、簡素化、外部ライブラリの利用など)
  • 選抜テーマ (読みやすいテストと実際のコードを使ったサンプル)

に別れている。

優れたコード

以下、優れたコードとは読みやすいコードという意味で書く。
読みやすいコードとは、コード(何をしているのか)が理解し易くメンテナビリティが高いと言える。

表面上の改善

名前の付け方

  • 名前のフォーマットに情報を持たせる。

汎用的な名前は避け、明確な名前を付ける

tmpやrtnなどの名前(本書では空虚な名前)は変数であれば何が代入されているか、関数であれば何をしているのか一見わからない。
このような名前は付けるべきではない。
ただし、これらの変数に明確な意味があれば別。

if ( left < right ) {
    let tmp = right;
    right = left;
    left = tmp;
}
// 本当に一時的に利用

「get」などの単語は色々な意味が含まれていて解釈に幅がある。
そのような単語を安易に使わずに、より具体的な(本書の中では「カラフルな」と記載)単語を利用することで誤解を減らせる。
例えば、fetch / download / etc.

名前は短いコメントだと思えば良い。
ただしやりすぎは禁物。
類義語辞典(シソーラス)などを利用すると良い。

重要な情報を付加する

代入されている値がどのようなものなのかが分かる属性を付加すると良い。例えば

暗号化する前のパスワードplantext_password
エスケープ前のコメントunescaped_comment
UTF8に変更したHTMLhtml_utf8

また単位もつけた方が理解しやすい。

ミリ秒time_ms
MBsize_mb

一般的でない略語は避け、不要な単語は捨てる

ConvertToStringなどはtoStringにしても情報は損なわれない。

フォーマットで伝える

例えばクラス名は大文字から始める。ローカル変数は末尾に「_」を付ける。などのように
命名規則をつけることで、それら名前がクラスなのか関数か、一時的なものか永続的なものかなどの判断ができる。

範囲

包括的範囲(閉区間)を指定するときはmax,min / first, lastを利用する。
包括/排他的範囲(閉区間/開区間)はbegin / endがよく使われているが、endは包括的な意味もあるので微妙

数学
閉区間 [1,4] -> 1≦x≦4 = 1以上、4以下
開区間 (1,4) -> 1<x<4 = 1超える、4未満

ブール値

ブール値を返す関数やブール値が代入されている変数などはtrue/falseの意味を明確にした方が良い。
変数の場合はis / has / can / shouldなどを付けてわかりやすくすることがある。
また否定形ではなく肯定形にした方が良い。

let use_ssl = true;

コメント

コメントの目的は「書き手の意図を読み取らせるもの」である。

記載すべきでないコメント

  • コードからすぐわかること
  • コメントのコメント

これらはコメントにすべきではない。
また名前が酷く、名前に対するコメントを付けるのであれば、名前を変えた方が良い。

コメントのTips

他の人(場合によっては将来の自分)がコードを読んだ時にどのように見えるか、どのようにコメントを書けば理解されるかを想像して書くと良い。
何故このよう実装にしたか、なぜその値を持っているのか等、監督コメンタリーのように記載すると良い。
ただし簡潔且つ正確に記載することを意識する。

// これまでにクロールしたURLかどうかによって優先度を変える
↓↓↓
// これまでにクロールしていないURLの優先度上げる

代名詞を使うと、何を指しているのかわからなくなる場合があるので名詞に置き換える。

// データをキャッシュに入れる。ただし、先にそのサイズをチェックをする。
↓↓↓
// データをキャッシュに入れる。ただし、先にデータのサイズをチェックをする。

ファイルやクラスの単位では「全体像」を記載し、処理のブロックでは、そのブロックの要約コメントを記載する。
また関数の動作は正確に記載する。

// このファイルに含まれる行数を返す。
↓↓↓
// このファイルに含まれる改行文字('\n')を数える。

何故これを実装したのかその必要性も併せて記載すると良い。
*つまりプログラマがコードを書くときに考えていたこと。

// listを逆順にイテレートする
↓↓↓
// 値段の高い順に表示する

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

  • 条件分岐やループ処理では条件式を見やすくする。
  • do/while文は条件がブロック下になり見にくいし大体の場合while文に置き換えが可能。
  • if文は肯定・単純・目立つものを優先的に処理し極力ネストを浅くする。
  • 論理式はド・モルガンの法則を利用する。(省略:高校数学とか参照。多分集合論の入り口あたり)
  • 関数は関数上部で単純な条件を先に処理するガード節などをうまく利用し、結果を早く返す。

極力流れが絶えないように意識する。

条件式の引数の並び方

比較条件式などを利用する場合は

左辺右辺
調査対象比較対象

とした方がよい。具体的には

○ if ( length <= 10 ) ...
× if ( 10 >= length ) ...

これは前者の方が英文法に法っている為

if the length is over 10.
if (   length     <=  10 )

式を分割する

説明変数、要約変数(本書用語?)を使う。

// line is
// [username]:[userID]
let username          = line.split(':')[0]; // 説明変数
let user_own_document = line.split(':')[1] === request.user.id; // 要約変数
if ( username === 'root' ) ...
if ( user_own_document ) ...

説明変数、要約変数がそれぞれ説明文や要約文の役割を果たすので、処理の流れを簡潔にすることができる。

コードの再構成

「エンジニアリングとは大きな問題を小さな問題に分割して、それぞれの解決策を組み立てること」
※ 本書引用。エンジニアリングとは何かと言われると明確かつ簡潔に答えられなかった。漠然としたイメージがあるだけだったが、答えを得れた感じ。

  • 無関係の下位問題を積極的に見つけて抽出する。
  • ユーティリティコードを作成する。(既存の関数がない場合に限る)
  • 既存インターフェイスが汚い場合などはラッパー関数を用意する。
  • 不要な関数の削除、標準ライブラリの利用。

無関係な下位問題とは

与えられた緯度経度に最も近い配列を返す「findClosetLocation(lat, lng, arr)」という関数があったとして
この関数の中に「2点間の緯度経度の球面距離を計算する」というコードが含まれているとすると、この関数の目標(高レベルの目標)は
「与えられた地点から最も近い場所を見つける」ということなので「2点間の緯度経度の球面距離を計算する」は下位問題(この関数が解決するべきではない問題)となる。この下位問題は抽出し汎用クラスにすることで疎結合になり、利便性も高まる。

選抜テーマ

選抜テーマに関しては特記することが無い、テストに関しては既にやっていることなので割愛。

感想

コードにしろイラストにしろ他の何にしろ目標を立て実践し失敗点・成功点を分析し次回に生かす。この繰り返しでより優れたプロダクトが出来ると思う。本書はサブタイトル「より良いコードを書くためのシンプルで実践的なテクニック」となっている通り次回に生かすためのヒントを与えてくれる良書だと思う。
ただ、経験が浅かったり未経験の場合、そも失敗点・成功点の蓄積が少ないからあんまり響かない気もする。

open close