#第一部 表面上の改善
##2. 名前に情報を詰め込む
名前つける対象:変数、関数、クラスetc
名前は短いコメントと思え。
名前に情報を詰め込む
2章の構成
- 明確な単語を選ぶ
- 汎用的な名前は避ける
- 抽象的な名前より具体的な名前
- 接尾辞や接頭辞を使って情報を追加
- 名前の長さを決める
- 名前のフォーマットで情報を伝える
###2.1 明確な単語を選ぶ
「空虚」な単語は避けるべき。「明確」な単語を検討しよう。
def GetPage(url):
...
どこからgetしてくるの?
インターネットからgetするのであれば、**FetchPage()やDownloadPage()**の方が明確
class BinaryTree{
int Size();
...
}
何のサイズなの?
ツリーの高さ?ノードの数?ツリーのメモリ消費量?
それぞれの意味合いに沿うように変更すると
TreeHeight()
NumNodes()
MemoryBytes()
などの候補があがる。
class Thread{
void Stop();
...
}
stopでもいいんだけど
動作に合わせてもっと明確な名前を
Kill(),Pause() etc
もっと「カラフル(明確)」な単語を探す
類語辞典を使って動作や状況によりマッチした単語を選択しよう。
行き過ぎた悪い例:PHPのexplode()とsplit()
気取った言い回しよりも明確で正確な方がいい。
###2.2 tmpやretvalなどの汎用的な名前を避ける
tmp,retval,fooなどの空虚な名前をつけずに、
エンティティ(実体)の値や目的を表した名前を選ぼう
var euclidean_norm = function(v){
var retval = 0.0;
for (var i = 0; i < v.length; i += 1)
retval += v[i] * v[i];
return Math.sqrt(retval);
};
retval自体には戻り値(return value)という情報しかない。
値の意味に則した名前をつけた方がベター。
vの2乗の合計を返しているのでsum_squaresを推奨。
retval += v[i];
// よりも
sum_squares += v[i];
// の方が2乗がないと気づける
retvalという名前には情報はない。変数の値を表すような名前を使おう。
tmp
// ここでのtmpは一時的な保管。しかも、生存期間は数行なのでtmpでもOK。
// 「この変数には役割がない」ことを明確に伝えている。
if(right > left){
tmp = right;
right = left;
left = tmp;
}
// 生存期間は短いが、「一時的な保管」ではない。
// user_infoが妥当。
String tmp = user.name();
tmp += " " + user.phone_number();
tmp += " " + user.email();
...
template.set("user_info", tmp);
# もしtmpのみであれば、ファイル名なのかデータなのか不明
tmp_file = tempfile.NamedTemporaryFile()
...
SaveData(tmp_file, ...)
tmpという名前は、生存期間が短くて、一時的な保管が最も大切な変数にだけ使おう。
ループイテレータ
i,j,k,iterなどの名前はインデックスやイテレータでよく使われる。
逆にそれ以外では使用してはダメ。
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[j] == users[k])
cout << "user[" << j << "] is in club[" << i << "]" << endl;
// i,j,jをci,mi,uiに変更するだけでミスを削減しやすくなる
for(int ci = 0; ci < clubs.size(); ci++)
for(int mi = 0; mi < clubs[i].members.size(); mi++)
for(int ui = 0; ui < users.size(); ui++)
if(clubs[ci].members[mi] == users[ui])
cout << "user[" << mi << "] is in club[" << ci << "]" << endl;
汎用的な名前のまとめ
tmp,it,retvalのような汎用的な名前を使うときは、それ相応の理由を用意しましょう
###2.3 抽象的な名前よりも具体的な名前を使う
任意のTCP/IPポートをサーバーがリッスンできるかを確認するメソッドの最適な名前は?
ServerCanStart() or CanListenOnPort()
例:DISALLOW_EVIL_CONSTRUCTORS
訳:悪のコンストラクタを禁止する
C++の仕様で、クラスはコピーコンストラクタと代入演算子を再定義しないと、デフォルトの設定が使われ、メモリリークなどのリスクがある。
#####問題
- C++の仕様を悪としてそのままEVILを使用していること
- コンストラクタを扱ってない
- 禁止(DISALLOW)する対象が具体的ではない
解決策
DISALLOW_COPY_AND_ASSINGN(コピーと割り当てを禁止する) に変更
例:--run_locally
あるコマンドのオプションとしての例。
実際にはプログラムがデバッグ情報(ログ)を表示するためのもの。
ただし、処理が遅くなるためローカルマシンでテストを実行する際に使用していた。
リモートではパフォーマンス重視なので使用しなかった。
#####問題
- 名前から処理内容を理解できない
- リモートでも動かすのにlocallyって変
- ローカルでパフォーマンステストを実行する際にオプションを使用しない場合もある
解決策
- --extra_logging(追加ログ)に改名
- 別の用途があるなら、無理に1つにまとめようとしないでオプションを増やして対応
###2.4 名前に情報を追加する
再喝:名前は短いコメント
// 例:af84ef845cd8
string id;
// 16進数の値が入っているなら以下の方がわかりやすい
string hex_id;
値の単位
時間やバイト数のように計測できるものであれば、変数名に単位を入れよう。
// msを返すため正常に動作しない
var start = (new Data()).getTime(); // ページ上部
...
var elapsed = (new Date()).getTime(); - start; // ページ下部
document.writeln("読み込み時間:" + elapsed + "秒");
var start_ms = (new Data()).getTime(); // ページ上部
...
var elapsed_ms = (new Date()).getTime(); - start_ms; // ページ下部
document.writeln("読み込み時間:" + elapsed_ms / 1000 + "秒");
#####改善例
- Start(int delay)
- Start(int dalay_secs)
- CreateCache(int size)
- CreateCache(int size_mb)
- ThrottleDownload(float limit)
- ThrottleDownload(float max_kbps)
- Rotate(float angle)
- Rotate(float degrees_cw)
その他の重要な属性を追加する
名前に付与する情報は単位だけではなく、
危険や注意を喚起する情報も含まれる
セキュリティが確保してない情報を含める場合は
untrustedUrl,unsafeMessageBody
セキュリティが担保された状態になれば
trustedUrl,safeMessageBody
と置き換える
#####改善例
- password
- plaintext_password
- comment
- unescaped_comment
- html
- html_urf8
- data
- data_unlenc
###2.5 名前の長さを決める
長い名前は避ける
#####デメリット
- 覚えにくい
- 画面を大きく占領
- 折り返しが必要でコード行が増える
####長い名前を避ける基準
- 変数の使い方によって違ってくる。
- ガイドラインはある。
スコープが小さければ短い名前でもいい
その変数が有効な範囲の広さ(名前が見えるコードの行数)
によって長さは変わってくる。
// 理解できるコードがすぐ近くにある
if(debug){
map < string, int > m;
LookUpNamesNumbers(&m);
Print(m);
}
// クラスのメンバ変数やグローバル変数はNG
LookUpNamesNumbers(&m);
Print(m);
長い名前を入力するのは問題じゃない
入力しにくいは理由にならない。
単語補完機能を使おう。
頭文字と省略形
例:BackEndManagerをBEManager
その省略内容を新人が理解できるか?
で判断すると使用基準がわかりやすい。
#####良い例
- evaluation
- eval
- document
- doc
- string
- str
BEManagerは悪い例。
不要な単語を投げ捨てる
#####良い例
- ConvertToString
- ToString
- DoServerLoop
- ServerLoop
###2.6 名前のフォーマットで情報を伝える
googleのフォーマット:
アンダースコア、ダッシュ、大文字で名前に情報を詰めている
static const int kMaxOpenFile = 100; // #defineマクロと区別:CONSTANT_NAME
class LogRender{ // クラスはキャメルケース
public:
void OpenFile(string local_file); // 変数はスネークケース
private:
int offset_; // 普通の変数と区別
DISALLOW_COPY_ASSIGN(LogRender);
}
// メンバ変数はmember_にする
// という規約を守っていたため、
// statsはメンバ変数ではなく、ローカル変数と判断できる
stats.clear(); // メンバ変数ならstats_となる
その他のフォーマット規約
// new で始まるコンストラクタは大文字で始まる
var x = new DataPicker();
// 通常の関数は小文字で始まる
var y = pageHeight();
// jQueryのオブジェクトを格納する変数は頭に$を付与
var $all_images = $("img");
// これはノーマル
var height = 250;
<!-- idはアンダースコア、classはハイフンが有力 -->
<div id="middle_column" class="main-content">
###2.7 まとめ
- 明確な単語を選ぶ
- tmpやretvalなどの汎用的な名前を避ける
- 具体的な名前を使って、物事を詳細に説明する
- 変数名に大切な情報を追加する
- スコープの大きな変数には長い名前をつける
- 大文字やアンダースコアなどに意味を含める