読みやすいコードの美学
短いコードのほうが優れているのか?
コードを減らすことで可読性は向上するが、コードを読む時間を短縮することこそが本質だ。
表層的な構造
- 明確なメソッド名と変数名の命名
- メソッド名に
doは不要 - 語彙を選ぶときは曖昧な名前を使わない(例: pop, popItem)
- メソッド名により多くの情報を含める
- メソッド名に
function getPage() {}
// 對方可能不知道 getPage 的實作方式?爬蟲? ajax?
function fetchPage() {}
// 可能比較清楚是用 ajax 的方式並且回傳 json。
- より的確な単語を選ぶ
- send => deliver dispatch announce route
- find => search extract locate recover
- start => launch create begin open
- make => create setup build generate add new compose
可愛らしさよりも、明確さのほうがずっと重要だ
-
たとえ
tmp変数であっても、もう少し情報を与えることができる。- tmpNumber
- tmpFile
- tmpUsrData
-
ループの i, j に意味があるなら、適切な名前をつける(例: row, col, index)。
-
具体的なメソッド名を選ぶ。
-
変数に単位があるなら、単位を含める。
- startSec
- delayMs
-
重要な属性を持つ変数の命名
- plainData
- entryptedData
-
変数のスコープが広い場合は、長め(またはより多くの情報を含む)の名前をつけるのが良い選択だ。逆に数行で終わるような短いスコープで、他人が見ても何をしているか一目でわかるなら、
alias(短い別名)を使っても問題ない。
誤解されない名前
filterは取り除くのか、それとも残すのか?min/maxの接頭辞- ブール値(boolean)
- computeData => コストの大きい関数を実行しているような印象を与える
一貫性のあるフォーマット
- 一貫性のあるフォーマットに合わせる
- 似た処理のコードの見た目を揃える
- 関連するコードを1つの段落にまとめる
- コメントの美学
なぜフォーマットがそれほど重要なのか?第一に、他の人(あるいは将来の自分)が後からコードを見たときに、読みやすく(そして読む気にもなりやすく)なるからだ。さらに、自分のコードが何をしているかを理解する時間を減らすことができる。やらない手はないだろう。
コード品質ツール
-
eslint
-
stylelint
メソッドを使って混乱を解消する?
何かをしているときに「ごちゃごちゃしている」と感じたら、メソッドとしてラップすべきだ。
綺麗だが中身のない包装で消費者を騙すのは人間の本性だ(嘘)。他人を騙せるかどうかは重要ではなく、肝心なのは自分自身を納得させられるかどうかだ。自分自身すら尊重できないコードを、他人が尊重してくれるはずもない。
例えば:
assert(checkTime("12:00")) === "12:00"
assert(checkName("kalan", 20)) === { name: "kalan", age: 20 }
assert(checkPaid(20000, true)) === 20000
上記のコードをよく観察してみると、どれも同じような処理をしており、重複した文字列も存在することがわかる。しかも冗長で、これらのコードが何をしているのかを理解するのに少し時間がかかってしまう。
こういうときこそリファクタリングが必要だ。メソッドでラップしてみよう。
function checkValue(type, value) {
if (type === "time") {
assert(checkTime(value));
}
if (type === "name") {
assert(checkName(value) === value;
}
if (type === "paid") {
assert(checkPaid(value)) === value;
}
}
// checkValue(type, value);
// [string] [depend]
checkValue("name", kalan);
checkValue("time", 12:00);
checkValue("paid", 20000);
これでコードがすっきりし、可読性も向上した! 改めて強調するが、単に短いコードが良いコードなのではなく、他人が理解しやすいコードこそが良いコードなのだ。 これ以外にも、以下のようなメリットがある:
- テストしている部分が明確になる。
- 他のテストを追加するのがずっと簡単になる!
順序と段落で区切る:
だらだらと長いコードは他人だけでなく、自分自身すら見たくなくなる。変数宣言や文を記述する際は、行っている処理の違いに応じて分割するといい。これは文章を書くときに段落を分けるのと同じ理屈だ。
function getUserInfo(userName, age) {}
getUserInfo("kalan", 20)
// getUserInfo(userName, age)
// [string] [number]
getUserInfo("kalan", 20)
同じような関数呼び出しが続く場合は、引数の位置を揃えると読みやすくなる。
command = {
{ "timeout" , null, cmd_spec_timeout},
{ "timestamping" , bull, cmd_adj_boolean},
f
}
コメントを書くときは、難しく考える必要はない。そのとき自分が考えたことや、この関数が果たすべき役割をそのまま書き残せば十分だ。時間が経つと、その関数が何をしているのか自分でも忘れてしまうことがあるのだから。
—
2. コメント編:
コメントは、コードを書いた人の意図を他人に理解してもらうために存在する。同時に、過去の自分が何を考えていたかを思い出すためでもある。
- 優れたコメントの書き方、そして何が「コメント不要」なのか。
- コメントすべきでない部分
- 読み手の立場に立って考える
コメントにコード本来の役割を奪わせてはならない
コメントを書くことへの抵抗感をなくそう。これにはある程度の慣れが必要だが、プロジェクトの構造が複雑化する前に早めにコメントを書いておくことは間違いなく良いことだ。さもなければ、良心に反して次のように書く羽目になる。
// TODO: refactor
そして二度と顧みられることはない。
まとめ
- 特定の書き方を選んだ理由
- コードの欠陥や妥協点
- 読み手がどの部分に疑問を抱きそうか
コメントを簡潔に保つ
- コードの動作をより正確に描写し、引数を説明する際に代名詞を使わない。
- 引数の挙動が複雑な場合は、具体例を挙げて一目でわかるようにする。
3. 制御フロー:
最も一般的なのは if/else の分岐だ。本書では「肯定的な条件を先に置く」「単純なケースを先に処理する」という指針が示されている。
関数やメソッドに戻り値があるなら、できるだけ早く return してしまおう(早期リターン)!
- ド・モルガンの法則を活用する:理系なら誰もが耳にしたことがあるはずだ。これを使えば複雑な論理判断をシンプルにできる。
複雑なロジックとの格闘:
本書で非常に興味深いアプローチが紹介されていたので、僕のメモとして記録しておきたい。
range(範囲)を実装する際、2つの range が重複しているかを判定する overlapWith というメソッドがあるとする。2つの range が重複しているかを直接判定するよりも、「重複していないこと」を判定するほうがずっと簡単だ。なぜなら、確認すべきケースは2つだけだからだ。すなわち「もう一方の end が range の前にある」か、「もう一方の start が end の後にある」かである。
巨大な式を変数に格納する
$(".thumb_up").removeClass("highlighted")
$(".thumb_up").removeClass("highlighted")
$(".thumb_up").removeClass("highlighted")
// refactor
const $thumbUp = $(".thumb_up")
const highLight = "highlighted"
$(".thumb_up").removeClass("highlighted")
$(".thumb_up").removeClass("highlighted")
$(".thumb_up").removeClass("highlighted")
//
4. 変数:
変数が生存するスコープや時間が長いほど、デバッグは難しくなる
- 不要な変数宣言を減らす。
不要な変数とは何か?
- 意図をより簡潔に表現できていないもの
- それ自体のロジックが複雑でなく、変数で置き換える必要がないもの
- 1回しか使われないもの
- 1度だけ書き込まれる変数(イミュータブルな変数)を使う
関数型プログラミングでは、関数が純粋(pure)でありイミュータブル(immutable)であることが望まれる。これは変数においても同様で、可能な限り変数は const かつ再代入不可にしておくべきだ。そうすることで関数の見通しが良くなるだけでなく、どこでバグが起きたかも把握しやすくなる。
考えをコードに落とし込む:
まずやりたい処理を言葉(日常会話のような口語)で説明し、その振る舞いをコードへと変換する。これによって、プログラマはより自然なコードを書くことができるようになる。
不要なコードを書くのを避ける
- 要件を理解する
- 要件を再考する
- 標準ライブラリへの理解を維持するために、定期的に API ドキュメントを読む
関連記事
- カーネギー 人を動かす How to win friends and influence people この本は名著中の名著であり、ずっと先延ばしにしていたが、ようやく読み終えた。本の中で伝えられている理念は、自分を利他主義者に変えていくようなものだと感じる。
- 『天才! 成功する人々の法則』(Outliers)Malcolm Gladwell 『天才! 成功する人々の法則(Outliers)』を読んだ感想
- 『So Good They Can't Ignore You(大事なことほど小声でささやかれる)』 by Cal Newport 「情熱」は過剰に美化されている。本書は冒頭、スティーブ・ジョブズのスピーチを切り口にして、ジョブズが実際に行ったことと言っていることは違うと直言する。情熱とは無理にひねり出すものではなく、何かを行っている過程で自然と生まれてくるものだと僕は思う。「僕には〇〇に対する情熱がある」と言っても、世間が求めているのは大抵あなたの情熱ではなく、あなたのアウトプットなのだ。
- MIT公開講座:Introduction to Computational Thinking with Julia 受講記 一見すると内容が雑多で、データサイエンス、気候変動モデリング、レイトレーシング、偏微分方程式、統計、画像処理などを網羅しているように見えるが……