本書の第1部「表面上の改善」にある2章のサマリーをまとめる。
2.1 明確な単語を選ぶ
getはあまり明確でない。
# このメソッドはページをどこから取ってくる?
def GetPage(url):
...
# インターネットから取ってくるのであれば、これらの方が明確
def FetchPage(url):
...
def DownloadPage(url):
...
sizeもあまり明確でない。
// このSizeメソッドは何を返す?
class BinaryTree {
int Size();
...
}
// ツリーの高さなら
class BinaryTree {
int Height();
...
}
// ノードの数なら
class BinaryTree {
int NumNodes();
...
}
// ツリーのメモリ消費量なら
class BinaryTree {
int MemoryBytes();
...
}
stopも怪しい。
// このThreadクラスのStopメソッドはどういう操作?
// もっと明確な名前の方がいい。
class Thread {
void Stop();
...
}
// 取り消しができない重い操作なら
class Thread {
void Kill();
...
}
// あとからResume()できるなら
class Thread {
void Pause();
...
}
もっと「カラフル」な単語を探す
シソーラス(類語辞典)を使って調べよう。
気取った言い回しよりも明確で正確な方がいい
2.2 tmpやretvalなどの汎用的な名前を避ける
エンティティの値や目的を表した名前を選ぼう。
retvalには「これは戻り値です」以外の情報はない。変数の値を表すような名前を使う。
ただ、汎用的な名前をうまく使った例もある。
汎用的な名前を使うときは、それ相応の理由を用意しよう。
// tmpという名前で問題ない
// 情報の一時的な保管が変数の目的で、生存期間も短く、明確に他に役割がない意味を伝えている
if (right < left) {
tmp = right;
right = left;
left = tmp;
}
// 以下のtmpは単なる怠慢
// user_infoのように変えるとわかりやすい
String tmp = user.name();
tmp += " " + user.phone_number();
tmp += " " + user.email();
...
template.set("user_info", tmp);
ループイテレータにもバグを見つけやすくする工夫がある。
// このコードは、member[]とuser[]のインデックスが逆になっている
// club_i, members_i, users_iや、ci, mi, uiなどにするとバグも目立つ
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])
cout << "user[" << j << "] is in club[" << i << "] << endl;
2.3 抽象的な名前よりも具体的な名前を使う
例えば
ServerCanStart()
(任意のTCP/IPポートをサーバがリッスンできるか確認するメソッド)
があったとする。
→ CanListenOnPort()
の方が具体的な名前で、メソッドの動作をそのまま表している
2.4 名前に情報を追加する
名前は短いコメントのようなもの。
絶対に知らせなきゃいけない大切な情報があれば、単語を変数名に追加する。
例えば、16進数の文字列を持つ変数
string id; // "af84ef845cd8"
これについて、IDのフォーマットが大切なら、名前をhex_id
にすると良い。
値の単位
時間やバイト数のように計測できるものがあれば、変数名に単位を入れる。
// getTime()は秒でなくミリ秒を返す!
var start = (new Date()).getTime();
...
var elapsed = (new Date()).getTime() - start;
document.writeln("読み込み時間" + elapsed + "秒");
// 変数名に_msを追加すれば明確になる
var start_ms = (new Date()).getTime();
...
var elapsed_ms = (new Date()).getTime() - start_ms;
document.writeln("読み込み時間" + elapsed_ms / 1000 + "秒");
単位を追加したより良いバージョンの仮引数の例には、delay_secs
, size_mb
, max_kbps
, degrees_cw
などがある。
その他の重要な属性を追加する
危険や注意を喚起する情報も追加した方がいい。
変数の意味を間違えてしまった時にバグになりそうなところにだけ使う。
状況 | 変数名 | 改善後 |
---|---|---|
passwordはプレインテキストなので処理をする前に暗号化すべき | password | plaintext_password |
ユーザが入力したコメントは表示する前にエスケープする必要がある | comment | unescaped_comment |
htmlの文字コードをUTF-8に変えた | html | html_utf8 |
入力されたデータをURLエンコードした | data | data_urlenc |
2.5 名前の長さを決める
スコープが小さければ短い名前でもいい
識別子のスコープ(その名前が見えるコードの行数)が小さければ多くの情報を詰め込む必要はない。
ある変数がクラスのメンバ変数やグローバル変数なら、名前が短すぎるとその変数の型や目的がよくわからず、コードが読みにくくなる。
長い名前を入力するのは問題じゃない
エディタの単語補完機能を使えば、長い名前でも入力しやすい。
|エディタ |コマンド |
|-----|-----|-----|
|Vi|Ctrl-p|
|Emacs|Meta-/|
頭文字と省略形
プロジェクト固有の省略形はダメ。(BackEndManager
をBEManager
とするなど)
新しいチームメイトはその名前の意味を理解できる?
不要な単語を投げ捨てる
ConvertToString()
をToString()
、DoServeLoop()
をServeLoop()
に変えても明確さは同じ。
2.6 名前のフォーマットで情報を伝える
エンティティごとに異なるフォーマットを使うことでコードを読みやすくできる。
// 定数はMACRO_NAMEのような#defineマクロと区別できるように
static const int kMaxOpenFiles = 100;
// クラス名はキャメルケース
class LogReader {
public:
void OpenFile(string local_file); // 変数名は小文字をアンダースコアで区切る
private:
int offset_; // クラスのメンバ変数は最後の文字をアンダースコアにし、普通の変数と区別
DISALLOW_COPY_AND_ASSIGN(LogReader);
};
その他のフォーマット規約
プロジェクトや言語によって使えるフォーマット規約は異なる。
自分自身やチームで規約の採否を決めるといい。プロジェクトで一貫性を持たせることが大切。