命名規則
明確な単語を選ぶ
「GetPage」だと何も分からない
- このメソッドはページをどこから取ってくる?
- ローカルキャッシュから?データベースから?
- インターネットから取ってくるなら、FetchPage()やDownloadPage()のほうが明確
Size()メソッドは何を返すか分からない
- ツリーの高さ?ノードの数?メモリ消費量?
- それぞれ、Height(), NumNodes(), MemoryBytes()になる
Stop()でもいいけど、もっと明確な名前をつけれる
- 取り消しができない重い操作ならKill()
- あとからResume()できるなら、Pause()
tmp や retvalなどの汎用的な名前を避ける
- 2乗の合計を返すなら、sum_squaresなど
- 一時的に保管する目的であればtempを使ってもいい
ループイテレータも明確な単語に
- i・j・k・iterなどの名前は、イテレータが複数ある時に複雑になる
- club_i, members_i, users_i または ci, mi, uiなどする
名前に情報を追加する
-
string id: // af84ef845cd8
→ IDのフォーマットが大事なら、hex_idが適切
値の単位
- 変数名に単位を入れる
- start → start_ms
- delay → delay_secs
- limit → max_kbps
- angle → degrees_cw
その他の重要な属性を追加する
- セキュリティ問題の多くは、プログラムの受診するデータが安全でないことが原因で発生している
- untrustedUrl や unsafeMessageBody などの命名をする
- データを安全にしたら、 trustedUrl や safeMessageBodyにするといい
- 具体
- password → plaintext_password
- comment → unescaped_comment
- html → html_utf8
- data → data_urlenc
名前の長さの決め方
- スコープが小さければ短い名前でもいい
- 単語保管機能を使えば、長い命名でも大丈夫
- プロジェクト固有の省略形の名前にしない
- 後から入った人が理解できる省略形なら大丈夫
- 不要な単語を投げ捨てる
- ConvertToString → ToString
- DoServerLoop → ServerLoop
曖昧な名前をつけない
fileter()はどんな値が返ってくるか曖昧すぎる
- 選択する → select()
- 除外する → filter()
Clip(text,length) も最後からlength文字を削除するのか、最大length文字まで切り詰めるのかわからない
- Truncate(text,length)に変えるほうがいい
- そしてlengthもmax_length、さらに言えば、単位をつけてmax_charsにするといい
限界値を含めるときはminとmaxを使う
- 未満なのか以下なのかわからないため、max_、min_を使う
範囲を指定するときはfirstとlastを使う
- stopは複数の意味に解釈できる
- lastをつかあえば、抱合していることが明確になる
包含/排他的範囲にはbeginとendを使う
- endは包括的を意味する
ブール値の名前
-
read_passwordだと二つの意味がありえる
- パスワードをこれから読み取る必要がある
- パスワードをすでに読み取っている
- → need_password, user_is_autheticatedを使う
- 頭にis・has・can・shouldをつける
-
-
名前を否定的にするのは避ける
- disabled_ssl = false → use_ssl = true
- 肯定形にしたほうが声にだして読みやすい
-
名前を否定的にするのは避ける
説明変数を使う
- 式を表す変数を使う
username = line.split(":")[0].strip();
コードを「段落」に分割する
def suggest new_friends(user, email_password);
# ユーザーの友達のメールアドレスを取得
friends = user.friend();
friend_emails = set(c.email for in friends);
# ユーザーのメールアカウントからすべてのメールアドレスをインポートする
contacts - import_contacts(user.email, email_password)
contact_emails
コメント
自分の考えを記録する
// このデータだとハッシュテーブルよりもバイナリツリーのほうが40%早かった
- コメントから情報を得ることができるので、下手に最適化しようとして無駄に時間を使う必要がなくなる
コードの欠陥にコメントをいれる
- 改善が必要なときは以下のように書いておく
// TODO: もっと高速なアルゴリズムをつかう
- コードが未完成のときは以下のように書いておこう
TODO(グラ): JPEG以外のフォーマットに対応する
- よく使う記法
- TODO : あとで手を付ける
- FIXME : 既知の不具合があるコード
- HACK : あまりキレイじゃない解決策
- XXX : 危険!大きな問題がある
定数にコメントをつける
- その定数が何をするのか、なぜその値を持っているのか「背景」が存在する場合が多い
// 合理的な限界値。人間はこんなに読めない
const int MAX_RSS_SUBSCRIPTIONS = 1000;
image_quality = 0,72; // 0.72ならユーザーはファイルサイズと品質の面で妥協できる
ハマりそうな罠を告知する
// 実行時間は0(タグの数 * タグの深さの平均)なので、ネストの深さに気を付ける
要約コメントをつける
def GenerateUserReport():
# このユーザのロックを獲得する
...
# ユーザの情報をDBから読み込む
...
制御フローを読みやすくする
条件式の引数の並び順
- 左側
- 調査対象の式。変化する
- 右側
- 比較対象の式。あまり変化しない
if(age >= 10){}
while(bytes_expected < bytes_expected) {}
- 「もし君が18歳以上ならば」という順番が自然
- 「もし君の年齢が年齢以下ならば」というのは不自然
if / elseブロックの並び順
- 条件は否定形よりも肯定形を使う。例えばif(!debug)ではなく、if(debug)を使う。
- 単純な条件を先に書く。ifとelseが同じ画面に表示されるので見やすい
- 関心を引く条件や目立つ条件を先に書く
if(a == b) {
// 第一のケース
} else {
// 第二のケース
}
以下のように書くのは同じ
if(a != b) {
// 第二のケース
} else {
// 第一のケース
}
関数から早く返す
- 関数で複数のreturn文を使ってもいい
ネストを浅くする
- ネストの深いコードは理解しにくい
早めに返してネストを削除する
- ネストを削除にするには「失敗ケース」をできるだけ早めに関数から返せばいい
ループ内部のネストを削除する
- 早めに返す技法はいつでも使えるわけではない。
- 早めに返すのと同じようなことをループ内部で行うには、continueを使う
for (int i = 0; i < result.size(); i++) {
if(result[i] == NULL) continue;
non_null_count++;
if(result[i]->name == "") continue;
count << : "Text" << endl;
}
変数の取り扱い
不要な変数の切り出しを削除する
- 変数が多いと変数を追跡するのが難しくなる
- 変数のスコープが大きいとスコープを把握する時間が長くなる
- 変数が頻繁に変更されると現在の値を把握するのが難しくなる
役に立たない一時変数
now = datetime.datetime.now()
root_message.last_view_time = now
- 複雑な式を分割していない
- より明確になっていない。datetime.datetime.now()のままでも十分に明確
- 一度しか使っていないので、重複コードの削除になっていない
中間結果を削除する
- 変数 index_to_remove は、中間結果を保持するためだけに使っている
var remove_one = function(array, value_to_remove) {
var index_to_remove = null;
for (var i = 0; i < array.length; i += 1) {
if(array[i] === value_to_remove) {
index_to_remove = i;
break;
}
}
if(index_to_remove !== null) {
array.splice(index_to_remove, 1);
}
}
- 結果をそのまま使えば、このような変数は削除できる
var remove_one = function(array, value_to_remove) {
for (var i = 0; i < array.length; i += 1) {
if(array[i] === value_to_remove) {
array.splice(i,1);
return;
}
}
}
- 関数から早く返すことで、index_to_removeをすべて削除できた。
- タスクはできるだけ早く完了するほうがいい
制御フロー変数を削除する
boolean **done** = false;
while(/* 条件 */ && !**done**) {
...
if(...) {
done = true;
continue;
}
}
- 制御フロー変数は削除できる
while(/* 条件 */) {
...
if(...) {
break;
}
}
変数のスコープを縮める
- クラスのメンバ変数を減らす
- メソッドをできるだけ static にするといい
- staticを使えば、メンバ変数とは関係ないことがよくわかる
変数は一度だけ書き込む
- const使うべき
- 変数が絶えず変更されると、値を追跡する難易度が高い
汎用コードをたくさんつくる
- ReadFileToString() や format_pretty()など、広く適用可能な関数を、複数のプロジェクトで再利用する
- 特別なディレクトリ(例: util)を用意する
一度に複数のタスクを行わない
var vote_changed = function (old_vote, new_vote) {
var score = get_score();
if(new_vote !== old_vote) {
...
}
if(new_vote === "Up") {
score += (old_vote === "Down" ? 2 : 1);
} else if (new_vote === "Down") {
score -= (old_vote === "Up" ? 2 : 1);
} else if (new_vote === "") {
score -= (old_vote === "Up" ? -1 : 1);
}
set_score(score);
}
- たった1つのこと(スコアを更新すること)をしているように見えて、実際には一度に2つのタスクを行っている
- old_vote と new_voteを数値に「パース」する
- score を更新する
2つのタスクを別々に解決すれば、読みやすいコードになる
// 最初のタスク(投票を数値にパースする)を解決したもの
var vote_changed = function (old_vote, new_vote) {
if(vote === "Up") {
return +1;
}
if(vote === "Down") {
return -1;
}
return 0;
}
// 2つめのタスク(スコアの更新)を解決している
var vote_changed = function (old_vote, new_vote) {
var score = get_score();
score -= vote_value(old_vote); // 古い値を削除する
score += vote_value(new_vote); // 新しい値を追加する
}