質問
メソッドのコメントを書く場合は、使用している場所で書くべき?メソッドの定義のところに書くべき?
読んでもわからなかったので知っている方教えてください。
# return なし
# 面積を表示
def printSquare(height, width):
print(height * width)
# 面積を表示
printSquare()
# return あり
# 面積を求める
def getSquare(height, width):
return height * width
# 面積を求める
square = getSquare(h, w)
print(square)
読む前に
自分なりに、リーダブルコードをまとめていますが、出来るだけ実際に本を買ってから読んでください。
作者さんのために、自分のためにも
この記事は、呼んだことがあるひとが見る用に作成しています。
リーダブルコード
リーダブルコードとは、コードとコメントを駆使して出来るだけ読みやすいソースコードを作成していくものです。
命名
命名する際には下記のルールを守る
- 明確な単語を選ぶ
- Getではなく、FetchやDownloadをなどを使う
- 誤解されない名前をつける
- 凡庸的な名前(tmp, retval, i, iter)を避ける(あるいは、使う状況を選ぶ
- 使いたい場合は、clubs[ci], members[mi], users[ui]のように少し調整する
- 抽象的な名前よりも具体的な名前を使う
- 接尾辞や接頭辞を使って情報を追加する
- 名前の長さを決まる
- 名前のフォーマットを情報で伝える
名前 | 形 | ルール |
---|---|---|
クラス | CamelCal | 単語の最初を大文字に |
変数 | lower_separated | 小文字でアンダースコアで区切る |
定数 | hoursInDay | 2個目の単語から頭文字を大文字に |
メンバ変数 | offset_ | 最後にアンダースコアをつける |
マクロ | DISALLOW_COPY_AND_ASSIGN() | 全て大文字でアンダースコアで区切る |
メソッド | pageHeight() | 2個目の単語から頭文字大文字 |
- 単位を追加する
- delay_secs, max_kbpsなど
- 属性を追加する
- html_utf8, unscaped_comment
- 不要な単語は捨てる
- ConverToString() -> ToString()
- 限界値
- 名前の前にmax_, min_をつける
- 範囲
- 名前の前にfirst, lastをつける(lastは含む
- 包含、排他的範囲
- 名前の前にbegin, endをつける(endは含まない
- ブール値
- is, has, can, shouldが使いやすい
- 否定形の名前にしない(disable_ssl)
命名時にしてはいけないこと
- get, setを乱用する
使うべき場面
コメント
コメントは無いのがベスト!
というよりは、無くてもわかるような変数やメソッドの命名して
優れたコーディングをすることでコメントはなくせる
書き方
前後にスペースを入れて綺麗にする
// コメント
/* コメント */
# コメント1
# コメント2
""" コメント """
コメントすべきこと
コメントはできるだけしない方がいいが、変数名などでは表現すべきでないこともあるので、コメントを使用する
そのコードへ至った理由、自分の考えを記録
他の実装方法もあるのに、わざわざその実装方法を取る理由をコメント
// "hash = (65599 * hash) + c"の高速版
hach = (hash << 6) + (hash << 16) - hash + c;
定数にコメント
なぜその値なのか、その定数が何をするのかをコメントする
// 合理的な限界値。人間はこんなに読めない。
const int MAX_RSS_SUBSCRIPTIONS = 1000;
NUM_THREADS = 8; // 値は「>= 2 * num_processors」で十分。
実例をだす
どの例にも対応できるような例を出す
// 実例:Strip("abba/a/ba", "ab")は"/a/"を返す
String Strip(String src, String chars)
引数にコメント
Pythonのように引数を名前付きで渡せるものはいいが名前をつけれない場合は、インラインコメントを使用する。
Connect(timeout = 10, use_encryption = False)
Connect(/* timeout = */ 10, /* use_encryption = */ false);
質問されそうなことを先読み
あまり知られていないことなどをコメントしておく
// ベクタのメモリを解放する(「STL swap 技法」で検索してみよう)
vector<float>().swap(data);
欠陥にコメント
# TODO : あとで手をつける(例:もっと高速なアルゴリズムを使う)
# 小さいものには小文字の"todo"を使ったり
# FIXME : 既知の不具合があるコード
# HACK : あまり綺麗じゃない解決策
# XXX : 危険! 大きな問題がある
とにかく書く
// やばい。これはリストに重複があったら面倒なことになる。
// 成形
// 注意:このコードはリストの重複を処理できません(実装が難しいので)。
コメントの手法
コメントでコードを書き、わかりやすく
// intはCategory
// pairの最初のfloadは'score'。
// 2つ目は、'weight'。
typedef hash_map<int, pair<float, float> > ScoreMap;
//CatecotyType -> (score, weight)
typedef hash_map<int, pair<float, float> > ScoreMap;
逆を考える
逆を考えることで、わかりやすくなることもある
# これまでにクロールしたURL華道家によって優先度を変える。
# 改善後
# これまでにクロールしていないURLの優先度を高くする
コーディング
小さくなくてもいい
下のコードの方が良い
assert((!bucket = FindBucket(key))) || !bucket->IsOccupied());
bucket = FindBucket(key);
if (bucket != NULL) assert(!bucket->IsOccupied());
変数の宣言の場所
宣言をまとめて先に行うのではなく、使う直前に宣言する
root_message= Messages.objects.get(original_id)
root_message.save()
all_relplies = Message.objects.select(root_id=original_id)
filterd_replies = []
変数は1地度だけ書き込む
2度3度変数を再定義していると、理解しにくく、トラブルになる傾向が少ない。
一貫性
縦のラインと、改行をうまく、使って見やすくする
public class PerfomanceTester {
public static final TcpConnectionSimulator wifi =
new TcpConnectionSimulator(
500, /* kbps */
80, /* millisecs latency */
200, /* jitter */
1 /* packet loss % */);
public static final TcpConnectionSimulator cell =
new TcpConnectionSimulator(
100, /* kbps */
400, /* millisecs latency */
250, /* jitter */
5 /* packet loss % */);
}
同じことはまとめる
上のコードは、同じようなことを複数回しているので、さらに簡潔にする
public class Performance Tester{
//TcpConnectionSimulator(throughout, latency, jitter, packet_loss)
// [kbps] [ms] [ms] [percent]
public static final TcpConnectionSimulator wifi =
new tcpConnectionSimulator(500, 80, 200, 1)
public static final TcpConnectionSimulator cell =
new tcpConnectionSimulator(100, 400, 250, 5)
段落ごとに分ける
def suggest_new_friends(user, email_password):
# ユーザーのヨモダ地のメールアドレスを取得する
friends = user.friends()
friend_emails = set(f.email for f in friends)
# ユーザーのメールアカウントから全てのメールアドレスをインポートする
contacts = import_contacts(user.email, email_password)
contact_emails = set(c.email for c in contacts)
# まだ友達になっていないユーザを探す
non_friend_emails = contact_emails - friend_emails
suggested_friends = User.objects.select(email_in = non_friend_emails)
# それをページに表示する
display['user'] = user
display['friends'] = frinds
display['suggeted_friends'] = suggeted_friends
return render("suggeted_friends.html", display)
制御フロー
- 比較の際には、左に変化する値を置く
if bytes_expected > bytes_received:
# 修正
if bytes_received < bytes_expected:
- if/else文を適切に並べる
- 肯定系 > 単純 > 目立つ ものが一般的
- 三項演算子、do/while文、goto文 は避ける
- 「ガード説」を使う
- 早めにreturn,breakする
- クリーンアップコードを使う
言語 | コード |
---|---|
C++ | デストラクタ |
Java, Python | try... finally |
Python | with |
C# | using |
ド・モルガンの法則を使う
"&&", "||"を見たらド・モルガンの法則を使って簡単にできないか疑う。
not(a or b) = not a and not b
not(a and b) = mpt a or not b
if(!(file_exist && !is_protected)) Error("SOrry, cound not read file.");
// 改良
if(!file_exists || is_protcted) Error("sorry, cound not read file.");
逆を考える
これは制御フローを簡単にする手法で、例えば重なる条件を考えるのではなく
重ならない条件を考えることでわかりやすくなるというもの
下位問題を抽出する
人るのメソッドなどには、1つの仕事をさせる。
複数ある場合は、それもまた新たなメソッドを作成する
これをすることで、再度同じメソッドを使用することができる
声に出す
問題や設計を声に出して、説明することで問題点を探すことができる
ライブラリを知り、使おう
たまには、標準ライブラリを読み、新しいコードを書いている際に、「これ見たことあるな」と思えるようにしよう。
エラーメッセージをわかりやすく
- Boost C++ライブラリなどを使う
- 自分で例外処理のエラーを文を書く
終わり
他にも重要なことが本には載っているので、気になるかたはご購入ください。
読んだ方がいいらしい書籍
高品質のコードを書くための書籍
- 「Code Complete 完全なプログラミングを目指して」 スティーブ・マコネル 著
- 「リファクタリング プログラムの体質改善テクニック」 マーチン・ファウラー 著
- 「プログラミング作法」 ブライアン・カーニハン 著
- 「達人プログラマー システム開発の職人から名匠への道」 アンドリュー・ハント 著
- 「Clean Code アジャイルソフトウェア達人の技」 ロバート・C・マーティン 著
プログラミングに関する書籍
- 「JavaScript:The Good Parts」ダグラス・クロックフォード 著
- 「Effective Java」ジョシュア・ブロック 著
- 「オブジェクト指向における再利用のためのデザインパターン」エリック・ガンマ 著
- 「数珠のプログラミング 本質を見抜いたアルゴリズムとデータ構造」 ジョン・ベントリー 著
- 「ハイパフォーマンス Webサイト 高速サイトを実現する14のルール」スティーブ・サウダーズ 著
- 「Joel on Software」 ジョエル・スポルスキー 著