所感リーダブルコード
・名前に情報を詰め込む
明確な単語を選ぶ
tmpやretvalなどの汎用的な名前を避ける
具体的な名前を使って、物事を詳細に説明する
スコープの大きな変数には長い名前をつける
大文字やアンダースコアなどに意味を込める
プロジェクト固有の省略形などは使用しない
計測できるものであれば、変数名に単位を入れる
・誤解されない名前
限界値を含める時はminとmaxを使う
範囲を指定する時はfirstとlastを使う(minとmaxでも代用可)
包含/排他的範囲にはbeginとendを使う
ブール値の変数名は、頭にis/can/has/shouldなどをつけることが多い
名前を否定形にするのは避け、肯定形で命名する
・コードの美しさ
読み手が慣れているパターンと一貫性のあるレイアウトを使う
似ているコードは似ているように見せる
関連するコードをまとめてブロックにする
同じコメントをいくつも並べない(P45参照)
一貫性のある簡潔な改行位置
同じ関数が何度も出てくる場合はメソッドを使った整列を行う
縦の線をまっすぐに整列する
一貫性と意味のある並び
ヒント:
対応するフォームのフィールドと同じ並び順にする
「最重要」なものから重要度順に並べる
アルファベット順に並べる
複数のコードブロックで同じようなことをしていたら、シルエットも同じようなものにする
ある場所でA/B/Cのように並んでいたものを他の場所でB/C/Aのように並べてはいけない。
意味のある順番を選び、常にその順番を守る。
空行を使って大きなブロックを論理的な「段落」に分ける。
・コメントするべきこと
コードからすぐにわかることをコメントに書かない
ひどい名前はコメントを付けずに名前を変える
自分の考えを記録する
コードの欠陥にコメントをつける
TODO: あとで手を付ける
FIXME: 既知の不具合があるコード
HACK: あまり綺麗じゃない解決策
XXX: 危険!大きな問題がある
定数にコメントを付ける
「実装の詳細」についてコメントを書くべきである
ハマりそうな罠を告知する
「全体像」のコメント
コメントすべきでは「ない」こと
・コードからすぐに抽出できること
・ひどいコードはコメントをつけるのではなくコードを修正する
記録すべき自分の考え
・なぜコードが他のやり方ではなくこうなっているのか
・コードの欠陥を示す
・定数の値にまつわる「背景」
読み手の立場で考える
・コードを読んだ人が「えっ?」と思うところを予想してコメントをつける
・平均的な読み手が驚くような動作は文章化しておく
・ファイルやクラスには「全体像」のコメントを書く
・読み手が細部に捕らわれないように、コードブロックにコメントをつけて概要を纏める
・コメントは正確で簡潔に
・複数の物を指す可能性がある「それ」や「これ」などの代名詞を避ける
・関数の動作はできるだけ正確に説明する
・コメントに含める入出力の実例を慎重に選ぶ(例:// 実例:Strip("ab","a")は"b"を返す
・コードの意図は、詳細レベルではなく、高レベルで記述する
× // Listを逆順にイテレートする ○ // 値段の高い順に表示する
・よくわからない引数にはインラインコメントを使う
(例:Function(/* arg = */ ...))
・多くの意味が詰め込まれた言葉や表現を使って、コメントを簡潔に保つ
・制御フローを読みやすくする
条件式(if)の引数の並び 左側…変化する 右側…あまり変化しない
(例 if(length >= 10))
条件は否定形よりも肯定形を使う
簡単な条件を先に書く
関心を引く条件や目立つ条件を先に書く
三項演算子はそれによって簡潔になる時のみ使う
三項演算子… 条件 ? a : b (= if(条件){a} else {b})
do/whileループを避ける
関数から早く返す
gotoは害のない時以外は使用しない
ネストを浅くする
変更するときにはコードを新鮮な目で見る。一歩下がって全体を見る。
ループ内部のネストを削除する。(continueを使う)
continue…通常if文と併用され、ループ処理の中である条件が成立する場合、それ以下の処理をスキップする
・巨大な式を分割する
説明変数
式を簡単に分割するには、式を表す変数を使えばいい
(× if line.split(':')[0].strip() == "root":
↓
(○ username = line.split(':')[0].strip()
if username == "root":)
要約変数
大きなコードの塊を小さな名前に置き換えて、管理や把握を簡単にする変数
(× if(request.user.id == document.owner_id){//文章編集可能}
…
if(request.user.id != document.owner_id){//文章は読み取り専用})
↓
(○ final boolean user_owns_document = (request.user.id == document.owner_id);
if(user_owns_document){//文章編集可能}
…
if(!user_owns_document){//文章は読み取り専用})
ド・モルガンの法則を使う
not(a or b or c) = (not a) and (not b) and (not c)
not(a and b and c) = (not a) or (not b) or (not c)
(× if(!(file_exests && !is_protected)) //処理;
↓
○ if(!file_exists || is_protected) //処理;
短絡評価の悪用
bool演算子 if(a || b)のaがtrueなら、bは評価されない
× assert((!(bucket = FindBucket(key))) || !bucket->IsOuupied());
↓
○ bucket = FindBucket(key);
if(bucket != NULL) assert(!bucket->IsOccupied());
(意味)「このキーのバケツを取得する。もしバケツがnullじゃなかったら、中身が入っていないかを確認する。」
「頭がいい」コードに気をつける。あとで他人がコードを読むときにわかりにくくなる。
イディオム
Python・JavaScript・Rubyなどの言語では、複数の引数の中から1つを返す「OR」演算子が使える(値がbool値になるわけではない)
(例) x = a || b || c
a・b・cの中から最初に「真」と評価出来るものを選ぶ。
例:複雑なロジックと格闘する
コードが複雑になってしまったら…
「反対」を考える!
同じ式を要約変数として関数の最上部に抽出する!
・変数と読みやすさ
変数を削除する
役に立たない一時変数
(例)now = datetime.datetime.now()
root_message.last_view_time = now
・複雑な式を分割していない
・より明確になっていない。
・一度しか使っていないので、重複コードの削除になっていない。
中間結果を削除する
中間結果を肘するだけのものは、結果をそのまま使えば良い。
→タスクはできるだけ早く完了するほうが良い。
制御フロー変数を削除する
×boolean done = false;
while(/* 条件 */ && !done){
…
if(…){
done = true;
continue;
}
}
↓
○while(/* 条件 */){
…
if(…){
break;
}
}
breakが使えないような複雑な場合は?
→コードやループ全体を新しい関数に移動すると良い!
変数のスコープを縮める
変数のことが見えるコード行数をできるだけ減らす。
メンバ変数→「ミニ」グローバル変数となってしまっているので、
ローカル変数にできるならローカル変数にしたほうが良い。
・メソッドをできるだけstaticにする。
staticメソッドを使えば「メンバ変数とは関係ない」ことがよくわかる
・大きなクラスを小さなクラスに分割する
分割後のクラスが独立していればいいが、クラスで相互にメンバを参照しあうようならやっても意味が無い。
C++のif文のスコープ
if文の中でのみ必要な変数は、if文の条件式で変数を定義すれば良い!
JavaScriptで「プライベート」変数を作る
×submitted = false; // グローバル変数
var submit_form = function(form_name){
if(submitted){
return; // 二重投稿禁止
}
…
submitted = true;
};
○var submit_from = (function (){
var submitted = false; //以下の関数からしかアクセスされない
return function(form_name){
if(submitted){
return; // 二重投稿禁止
}
…
sumitted = true;
}());
JavaScriptのグローバルスコープ
Javascriptでは変数の定義にvarをつけないと、グローバルスコープに入ってしまう。
→全てのJSファイルや<script>ブロックからアクセスできてしまい危険。
変数を定義するときには常にvarキーワードをつける!(var x = 1)
ネストしないスコープ
for・if・tryなどのブロックで定義された変数は、スコープがそのブロックに制限される。
定義の位置を下げる
変数の定義は変数を使用する直前で良い
変数は一度だけ書き込む
変数を操作する場所が増えると、現在地の判断が難しくなる。
無関係の下位問題を抽出する
自己完結しているコードを抽出し、別の関数にする
既存のインタフェースを簡潔にする
理想とは程遠いインタフェースに妥協することはない
でもやり過ぎはよくない!
・一度に一つのことを
・コードは1つずつタスクを行うようにしなければいけない
タスクは小さく出来る
デフォルト値を設定して、値が見つかったら書き換える!
・コードに思いを込める
・ロジックを明確に説明する
・ライブラリを知る
・解決策を言葉で説明する
・短いコードを書く
・プロジェクトに欠かせない機能を過剰に見積もってしまうことに注意
・質問と要求の分割
要求を詳しく調べれば、問題を簡単にできることもある。
・身近なライブラリに親しむ
たまには標準ライブラリを読もう
・テストと読みやすさ
テストを読みやすくて保守しやすいものにする
テストコードが大きいと…
→本物のコードを修正することを恐れる
→新しいコードを書いた時にテストを追加しなくなる
テストを読みやすくする
大切ではない詳細はユーザから隠し、大切な詳細は目立つようにするべき
最小のテストを作る
テストの本質は「こういう状況と入力からこういう振る舞いと出力を期待する」のレベルまで要約できる。
つまり、テストステートメントというものは大抵一行にまとめることが出来る。
独自の「ミニ言語」を実装する
・エラーメッセージを読みやすくする
良いassertを使おう
手作りのエラーメッセージ
エラーメッセージはできるだけ役に立つようにする
良いエラーメッセージがなければ作るのが近道なこともある
・テストの適切な入力値を選択する
コードを完全にテストする最も単純な入力値の組み合わせを選択しなければならない
入力値を単純化する
テストには最も綺麗で単純な値を選ぶ
ひとつの機能に複数のテスト
コードを検証する「完璧」な入力値を1つ作るのではなく、小さなテストを複数作るほうが簡単で、効果的で、読みやすい
複数のテストで別々の方向からバグを見つけ出すようにする。
→テストケースが分割されていれば、次の人がコードを扱いやすくなる
・テストの機能に名前をつける
テストコードの関数にはTest1()やTest2()などの名前ではなくTest_<関数名>()とする。
次に、状況に応じてこのテスト関数を分割する場合は、Test_<関数名>_<状況>()という形式にすれば良い。
・テストに優しい開発
テスト容易性の低いコード
・グローバル変数を使っている
・多くの外部コンポーネントに依存している。
・コードが非決定的な動作をする。
テスト容易性の高いコード
・クラスが小さい、あるいは内部状態を持たない
・クラスや関数が1つのことをしている
・クラスは他のクラスにあまり依存しない
・関数は単純でインタフェースが明確である