· 6分で読了

読みやすいコードの美学

この記事は中国語から自動翻訳されたものです。翻訳によりニュアンスが失われている場合があります。

短いコードのほうが優れているのか?

コードを減らすことで可読性は向上するが、コードを読む時間を短縮することこそが本質だ。

表層的な構造

  1. 明確なメソッド名と変数名の命名
    • メソッド名に do は不要
    • 語彙を選ぶときは曖昧な名前を使わない(例: pop, popItem)
    • メソッド名により多くの情報を含める
function getPage() {}
// 對方可能不知道 getPage 的實作方式?爬蟲? ajax?
function fetchPage() {}
// 可能比較清楚是用 ajax 的方式並且回傳 json。
  1. より的確な単語を選ぶ
    • send => deliver dispatch announce route
    • find => search extract locate recover
    • start => launch create begin open
    • make => create setup build generate add new compose

可愛らしさよりも、明確さのほうがずっと重要だ

  1. たとえ tmp 変数であっても、もう少し情報を与えることができる。

    • tmpNumber
    • tmpFile
    • tmpUsrData
  2. ループの i, j に意味があるなら、適切な名前をつける(例: row, col, index)。

  3. 具体的なメソッド名を選ぶ。

  4. 変数に単位があるなら、単位を含める。

    • startSec
    • delayMs
  5. 重要な属性を持つ変数の命名

    • plainData
    • entryptedData
  6. 変数のスコープが広い場合は、長め(またはより多くの情報を含む)の名前をつけるのが良い選択だ。逆に数行で終わるような短いスコープで、他人が見ても何をしているか一目でわかるなら、alias(短い別名)を使っても問題ない。

誤解されない名前

  1. filter は取り除くのか、それとも残すのか?
  2. min / max の接頭辞
  3. ブール値(boolean)
  4. computeData => コストの大きい関数を実行しているような印象を与える

一貫性のあるフォーマット

  1. 一貫性のあるフォーマットに合わせる
  2. 似た処理のコードの見た目を揃える
  3. 関連するコードを1つの段落にまとめる
  4. コメントの美学

なぜフォーマットがそれほど重要なのか?第一に、他の人(あるいは将来の自分)が後からコードを見たときに、読みやすく(そして読む気にもなりやすく)なるからだ。さらに、自分のコードが何をしているかを理解する時間を減らすことができる。やらない手はないだろう。

コード品質ツール

  • 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. 意図をより簡潔に表現できていないもの
  2. それ自体のロジックが複雑でなく、変数で置き換える必要がないもの
  3. 1回しか使われないもの
  • 1度だけ書き込まれる変数(イミュータブルな変数)を使う

関数型プログラミングでは、関数が純粋(pure)でありイミュータブル(immutable)であることが望まれる。これは変数においても同様で、可能な限り変数は const かつ再代入不可にしておくべきだ。そうすることで関数の見通しが良くなるだけでなく、どこでバグが起きたかも把握しやすくなる。

考えをコードに落とし込む:

まずやりたい処理を言葉(日常会話のような口語)で説明し、その振る舞いをコードへと変換する。これによって、プログラマはより自然なコードを書くことができるようになる。

不要なコードを書くのを避ける

  • 要件を理解する
  • 要件を再考する
  • 標準ライブラリへの理解を維持するために、定期的に API ドキュメントを読む

関連記事

他のトピックを探索