はじめに
こんにちは。アメリカに住みながら、独学でエンジニアを目指している Taira です。
本日はリーダブルコードを読んで、未経験の私でさえ重要そうだなと思った個所についてまとめてみました。
最初は英語版を読んでいたのですが、30 分で 2~3 ページしか進まなかったので途中日本語版に切り替えて読みました。
なお、すぐに反映できるような 1 章と 1 部の 2~6 章までについて書いております。
1 章 理解しやすいコード
読みやすさの基本定理
コードは他の人が最短時間で理解できるように書かなければいけない。
読みやすいコードというのは、あくまでも第三者が時間がかからず読めるようなコードであることが根底にあるよという事でした。
1 章はサクッと終わりましたが、根底の考え方って本当に大事なのでこれは守るべきだなと思いました
2 章 名前に情報を詰め込む
明確な単語を選ぶ
以下のコードでは stop という単語を使用していますが、もう少し動作に合わせて明確な名前を付けるべきであると本書には書かれています。
取り消しができない思い操作ならKill()
,後から再開できる操作ならPause()
でもよさそう。
class Thread {
void Stop();
...
};
ループイテレータ
これはどうするべきなのだろうと、疑問に思っていたことなので指標があるととても助かります。
例えば以下のコードについて
for (int i = 0; i < clubs.size(); i++)
for (int j = 0; j < clubs[i].members.size(); j++).
for (int k = 0; k < users.size(); k++)
if (clubs[i].members[k] == users[j]) // 正しくはmembers[j]とusers[k]
cout << "user[" << j << "] is in club[" << i << "]" << endl;
上記のようにi
,j
,k
のそれぞれの意味がわからないとこのようなバグが発生してしまいます。
今回の場合は、i
,j
,k
ではなく説明的な名前(club_i
,members_i
,users_i
)とするか簡潔な名前(ci
,mi
,ui
)とするだけで可読性も上がるだろうということでした。
名前に情報を追加する
教材で出てくるので理解はしていたのですが、改めて説明されるとなるほど!という感じでした
例えば、java においてstring id; // 例: 'fad54fa4fa64'
のような 16 進数の文字列の場合は hex_id
とするのがよい
値の単位
var start = (new Date()).getTime(); // ページの上部
...
var elapsed = (new Date()).getTime() - start; // ページの下部
document.writeln(" 読み込み時間:" + elapsed + " 秒 ");
例えば上記のコードについて、想定しているような出力にはならないようです。なぜなら getTime は秒ではなくミリ秒をとるからだそうです。
なので、コードを見た際にミリ秒(ms)ということがわかるように記述するほうがいいようです
var start_ms = (new Date()).getTime(); // ページの上部
...
var elapsed_ms = (new Date()).getTime() - start_ms; // ページの下部
document.writeln(" 読み込み時間:" + elapsed_ms / 1000 + " 秒 ");
その他の重要な属性を追加する
単位だけでなく、危険や注意を喚起する情報も追加するべきだと本書には書かれています。
password
という変数が平文(plaintext
)ので暗号化されていない場合はplaintext_password
,これからエスケイプが必要な変数にはraw_
を付けるなどです。raw_
をつけ忘れてしまって、エスケイプする実装をしなかった場合に XSS 等のセキュリティ脆弱性が発生し、大きな損害を負う可能性があります
3 章 誤解されない名前
限界値を含めるときは min と max を使う
amazon のようなショッピングサイトを想像した際にショッピングサイトのカートが 10 個までしか入らないとします
CART_TOO_BIG_LIMIT = 10
if shopping_cart.num_items()>= CART_TOO_BIG_LIMIT:
Error('カートにある商品数が多すぎます')
ここで問題なのが、上記の式では 10 個以上となるつまり、カートに 10 個いれたときにエラーが出てしまいます。
本来はおそらく、11 個目を入れたときにエラーを返すのが正しい挙動なはずです。
また、CART_TOO_BIG_LIMIT という言葉が曖昧過ぎて境界値を含むのか否かの記述が書いていないです。
なので正しく書き直すと
MAX_ITEMS_IN_CART = 10
if shopping_cart.num_items()> MAX_ITEMS_IN_CART: // =を外した
Error('カートにある商品数が多すぎます')
個人的にはMAX_ITEMS
だけでもよさそうですが、境界値を含む最大値といいたい場合は MAX を使いましょうということです。
包含/排他的範囲には begin と end を使う
10 月 16 日に開催されたイベントをすべて印字したいとするとき
PrintEventInRange("Oct 16 12:00am","Oct 17 12:00am")
このときの仮引数として選ぶとすると何が適切かという話ですが、ここでは begin,end を使うようにしようという話です。
包含/排他的範囲というのは始めの値(含む)は begin,終わりの値(含まない)は end であらわします。
ブール値の名前
ブール値というのはtrue
か false
を持つ値のことを指します。
データベースをやったりするとboolean
とか見たことあるかと思います。
ruby の場合はauthenticate?
のように?をつけるのが一般的ですが、JavaScript などほかの言語の場合、変数の頭に is
,has
,can
,should
等をつけるべきであると書いてあります。
この並びでhas
が来たので最初は現在完了のhas
なのかなと思っていましたが、ここでは持っているhave
の三単現のようです。
4 章 美しさ
一貫性と意味のある並び
これは当たり前ではありますが、改めての確認という感じです
例えば、以下のようは 5 つの変数があったとします
details = request.POST.get('details')
location = request.POST.get('location')
phone = request.POST.get('phone')
email = request.POST.get('email')
url = request.POST.get('url')
どんな順番で並べてもかまわないが、意味のある順番に並び替えるといいと書いてありました
- 対応する HTML フォームの
<input>
フィールドと同じ並び順にする。 - 「最重要」なものから重要度順に並べる。
- アルファベット順に並べる
私はアルファベット順に並べるよりも、ほかのコードと対応するように書くようにしています。
もし、並び順を変えてしまうと以下のようにわかりずらくなってしまいます
if details: rec.details = details
if phone: rec.phone = phone // 'location' はどこ?
if email: rec.email = email
if url: rec.url = url
if location: rec.location = location // なぜ'location' が下にあるの?
5 章 コメントすべきことを知る
ひどい名前はコメントをつけずに名前を変える
ここまで要約を見た人の中には「別に命名にこだわらず、コメントに補足を書けばコード命名にこだわらなくてもいいのでは?」
と思った人もいるかもしれません。ここまで極端ではありませんが、わたしも最悪忘れたとしてもコメントで補おうと考えました。
しかし、本書では以下のように書いてあります
通常は「補助的なコメント」(コードの読みにくさを補うコメント)が必要になることはない。プログラマはこのことを「優れたコード > ひどいコード + 優れたコメント」と言っている。
正直これをみてドキッとしました。見透かされているような感じで、気が引き締まる章でした。
コメントはあくまでも補助的に。含められる情報は変数にという意味合いが含められていると思いました
コードの欠陥にコメントをつける
チーム開発をしている際に、そのコードに改善が必要な場合があったとします。その時は以下のようにコメントしてみてください
//TODO: もっと高速なアルゴリズムを使う
またタケシさんに御願いする場合は以下のように書くことができます
// TODO( タケシ ): JPEG以外のフォーマットに対応する
本書には一般的にこのような意味合いがあると定義されています
記法 | 一般的な意味合い |
---|---|
TODO: | あとで手を付ける |
FIXME: | 既知の不具合があるコード |
HACK: | あまりきれいじゃない解決策 |
XXX: | 危険!大きな問題あり |
これは会社によってまちまちなのでそのルールに合わせるようにしましょう |
6 章 コメントは正確で簡潔に
関数の動作を正確に記述する
ファイルの行数を数える関数を書いているとします
// このファイルに含まれる行数を返す。
int CountLines(string filename) { ... }
上記のコメントは正確でないといえます。というのも以下のケースはどのように考えるか悩んでしまうからです
- ""(空のファイル)は、0 行なのか 1 行なのか。
- "hello"は、0 行なのか 1 行なのか。
- "hello\n"は、1 行なのか 2 行なのか。
- "hello\n world" は、1 行なのか 2 行なのか。
- "hello\n\r cruel\n world\r" は、2 行なのか 3 行なのか 4 行なのか。
最も簡単な実装は改行文字(\n)を数えるもので、この実装に適したコメントは以下の通りです
// このファイルに含まれる改行文字('\n')を数える。
int CountLines(string filename) { ... }
そこまで長くならず、上記の問題点を解消することができました。
まとめ
未経験ながら重要そうな tips をまとめてみました。
今回は 6 章までまとめてみましたが、これは即効力のある tips というのもあるのですが、これ以降の内容がやや実践向きとなるのと、Java のコードが多くなり何が何だか負えなくなってきます。
7 ~ 9 章まではギリギリ読めたのですが、10 章はイメージがわかなかったのでエンジニアになってからまた読み返したいと思います。
とはいっても 7 章のループについての tips はとてもためになるので、ぜひ見てみてください。
リーダブルコードは英語版であればネットに無料で落ちているのでよかったら探してみてください。
また、有料ですがありがたいことに PDF 版もあるので(私はそれを買いました)よかったら探してみてください