リーダブルコード
前に読んだけどおさらいで要点をまとめて気楽に見返せるようにしておく。
1, 2章
シソーラスを使う
類語辞典のこと。よりカラフルな単語を探す
getという曖昧な単語よりも、downloadやfetchと言う単語を使うほうがより明確になるときはそちらを使う
イテレータにも名前をつける
iとかjとかkとか、for文で回す単語
members_iとか、使う配列に明確な名前をつけておくと良い
miとかでもOK
変数名の考え方
明確な単語を選ぶ
汎用的な名前は避ける
抽象的よりも、具体的
名前の長さ
名前のフォーマットで情報を伝える
情報の詰めかた
16進数のID変数があったとしたら、idではなくhex_idと宣言すべき
単位をつける
getTime()という関数が秒ではなく、ミリ秒を返したとしたら?
格納する変数はsample_msと言うように単位をつけておいてもよい
重要な情報属性を追加する
生のパスワードを入れている変数
passwordよりも、plaintext_passwordの方がよい
何でもかんでもつけるのではなく、バグになりそうなところに使う
名前の長さについて
スコープが小さければ短い名前でもよい
使う範囲が狭い変数という解釈でOK
長い名前のデメリットの「入力が面倒」という点について
テキストエディタの補完機能で十分に補える
適当な省略はしない
BackEndCalculate->BECalculateという感じで勝手に省略するのは良くない
不要な単語は投げ捨てる
ConvertToString()
ToString()にしても全然通じる
3章 誤解されない名前
包含的な範囲の指定
start, stopだと、start / stopの値を含めるか否かが分からない
begin, endにしておくと理解しやすいかも
否定系を使わない
disable_flagとか、そういうの
重い関数には相応の名前をつける
getProfile()という関数
全DBからプロフィールを取得する関数だった場合処理が非常に大きい
AcquireAllUserProfile()とかの方が重い処理っぽい響きになる
4章 美しさ
適切な改行を入れる
クラスの定義などで長い文面があったとして
さらに変数も長かった場合、微妙な場所で折り返しが起こる
事前にクラス定義などにインデント込みの改行を仕込んでおき、バランスをとっておく
メソッドを使った整列
code:php
$is_get_of_instance_sample_exist_paid_new_user = assert(TestFunctionOfUserFullNameAndCustomerData, 100);
$newUser = assert(TestFunctionOfUserFullNameAndCustomerData, 100);
こんな感じで文字の長さが違うと横幅次第でズレる
メソッドを使って整列しよう
code:php
$is_get_of_instance_sample_exist_paid_new_user = AcquireFullData();
$newUser = AcquireFullData();
縦に揃える
code:php
// 見やすいし、Typoも気づける
$is_get_of_instance_sample_exist_paid_new_user = AcquireFullData();
$newUser = AcquireFulllData();
// ↑ここが違うのが気付きやすい
引数も揃える
code:php
$x = addData(1 , 10 , true);
$magic_number = addData(1000, 250, false);
一貫性を持たせる
code:php
/* 既存コードの部分 */
$max_num = addData(1,10,true);
$sample_add_data = addData(1000,250,false);
if ($is_ready_to_fight)
{
//
}
/* 新規記述コード */
$x = addData(1 , 10 , true);
$magic_number = addData(1000, 250, false);
if ($is_ready_to_fight) {
//
}
関数の使用文も、if文の括りにも一貫性がない
既存コードに合わせた書き方をするか、既存コードのフォーマットも整えるような書き方をすべき
5章 コメントすべきこと
⚠️コメントすべきではないこと
意味そのままを書くようなものは不要
code:php
// Animalクラスの定義
class Animal {
コードを日本語訳しただけのもの
code:php
// 渡された$aと$bの値を合計した値を返す
function addSumData($a, $b) { ... }
これなら、どんなときに使うかを書いた方がいい
code:php
// ユーザーの既存課金額, 新規課金額の合算値
// TODO: $a, $bという引数も上記に相応しい変数名に変える。
function addSumData($a, $b) { ... }
自分の考えを記録する
code:php
// fputcsv()も使用可能だが、カンマが強制的に3つ付くバグ(仕様?)があるため、fwriteで書き込み
欠陥にコメントをつける
code:php
// TODO: 関数でカプセル化する
読み手の立場になる
自分が新しくコードを触るときに、ハマりそうな罠を考えてみる
code:php
// このカラムはis_paying_user = trueの時のみ使用される
「無いよりはマシ」のコメントを書く
code:php
// 外部サービスのメールアプリを呼び出す
// 内部メールサービスとは無関係
sendEmail(string $msg, bool $is_paying_user) { ...
まずは気持ちのまま書いて、それっぽく清書する
code:php
/* DBの本全部は取らんでいい */
$books = Book::get();
->
// TODO: 全書籍の情報を取得してしまっているので改善できる。ここで扱うデータは1000円以下, 中古の書籍データのみ。
6章 コメントは正確で簡潔に
領域に対する情報の比率を上げる
代名詞を避ける
//データをキャッシュする(先にそのサイズをチェックする)。
→先にデータのサイズをチェックすると素直に書いてあげる
実例を書く
code:php
// Strip("abba/a/ba", "ab") は、 「/a/」を返す
function Strip(string src, string chars): string {
...
}
丁寧なコメントでも視覚化が難しければ、例を使って書いてやる
7章 制御フロー
三項演算子
無理して使わず、単純な処理に関してのみは読みやすい(amかpmかどちらの時刻か、など)
ネストを浅くする
code:c
if (user_result == SUCCESS) { //3
if (per_result != SUCCESS) { // 1
reply.WriteErrors("パーミッションerrorです");
reply.Done();
return;
}
reply.WriteErrors("");
} else { // 2
reply.WriteErrors(user_result);
}
reply.Done();
// ---------- 失敗ケースを早めに返すように書く ------------
if (user_result != SUCCESS) { // 2
reply.WriteErrors(user_result);
reply.done();
return;
}
if (per_result != SUCCESS) { // 1
reply.WriteErrors(permission_result);
reply.Done();
return;
}
reply.WriteErrors(""); // 3
reply.Done();
if-elseで入れ子で無理に書かず、最後の文章がreturnで終われないかを意識する
コードの深さが減る
比較の値の書き方
左に変化しうる値を置く
code:php
if ($x > 10) { ...
if (10 < $x) { ... // なんとなしに読みづらい
8章 巨大な式を分割する
説明変数
長い式を短くするために一時的に格納された値
code:php
if (isset(User::where('user_type', 1)->get()) {
...
}
// 一時的に格納したほうが読みやすい
$payment_user = User::where('user_type', 1)->get();
if (isset($payment_user)) {
...
}
9章 変数と読みやすさ
説明変数の使い方
code:php
$now = Carbon::now()
if ($time >= $now) { ...
Carbon::now()で十分意味が通じるので、格納する必要がない
変数の書き込みは1度に抑える
code:php
if ($x == 0) $x = 100 + $y;
$x = $x + $z; // これなら上の式に書き足したりして対応すれば良い
変数のスコープを小さくする
改修時に改修対象の行が少なくなる
10章 無関係の下位問題を抽出する
問題を突き止める
関数やコードブロックを見て、このコードの高レベルの目標はなんなのか考える
各行に対して疑問を持つ
高レベルの目標に効果があるのか
無関係の下位問題を解決しているのか
下位問題が多くあるならば、それらを抽出して別の関数としてまとめるべき
下位問題とは
地球のある地点を見つける関数があったとする
その中で余弦定理を使って計算式が書かれていた
この計算式は「地球のある地点を見つける」という目的とはズレたものなので、別関数とすべき
「余弦定理」の部分を下位問題として捉え、別関数としてまとめることで将来再利用することもできる
アプリケーションとは独立した関数となる
ユーティリティコードを作る
ファイルを読み込むコードなどは、よく使う
phpならfile_get_contents()
用意されていないが、「こんな関数があればな」と思ったら自分でユーティティなコードを作ってしまう
汎用コード
JSでコードを書くとき、デバッグ用にconsole.logやalertを使う
その際に表示の体裁を整えるために_やスペース、改行でいい感じにすることがある
→これは下位問題に該当するので、体裁を整えるための関数を作ってしまえば良い
その場限りで使い捨てのコードになることなく、今後も使うことができる
またその関数自体のアップデートも容易になる
トップダウン、ボトムアッププログラミング
先に高レベルのモジュールや関数を設計してから、低レベルの関数を実装していくか
それとも先に全ての下位問題を解決してから、それを使う関数を設計していくか
下位と上位の問題を切り分けられているかが重要になる
11章 1度に一つのことを
1度に複数のことをするコードは理解が難しい
データの不足分を入力し、ソートし、特定の値を返すような関数は全体像の把握に時間がかかる
入力のパース
文書を構文解析すること
男を0として表すのも、パースと言える
12章 コードに想いを込める
イテレート
反復、繰り返し
pythonでは、複数のデータを持った型から値を抜き取ること
プログラムを簡単な言葉で書く
わからない人に伝えるように、簡単な言葉でやりたい処理を書いてみる
13章 短いコードを書く
質問と要求
現在地の緯度と経度を取得し、現在地から最も近い店舗を出力するようにしたい
→日付変更線は、北極南極の場合は...
など難しいことを考えず、その店舗がローカル店舗ならば現在地から全ての店舗の距離を算出して求めれば良い
プロジェクトが成長するまでにやること
汎用的なユーティリティコードの実装
未使用、不要な機能の削除
コードを消したくない問題について
勿体無いが、そのコードが別の問題を生むこともあるので後々のことを考えるとさっさと消しちゃおう
プロジェクトをサブプロジェクトに分解する
ユーザーの重量を意識する(動作感など)
身近なライブラリを親しむ
たまには公式ドキュメントを眺めよう
そうすると仕事の時、「こんなことできた気がする」と思い出すことができる
1から作るという発想になりにくい
14章 テストと読みやすさ
テストの原則
読みやすく、修正しやすくあること
本番コードの修正の時、「テストを修正するのがめんどくさい」となったら本末転倒
エラーメッセージをassert()で定義する
手作りのエラーメッセージでテストの意図を分かりやすいものにする
テストの名前
テストする対象、状況やバグなどを入れる
Test_<関数名>などでも良い
呼び出されるものでもないので、名前は長くなってもオッケー
テストしやすいような本番コードを書けば、自然に綺麗になっていく
やり過ぎ注意
100%全部カバーしようとする
残りの5%を埋める時間より、本番コードを見て理解する時間の方が早いとか