リーダブルコードの内容をわかりやすくまとめてみた
エンジニアとして影響を受けた技術書ランキング2020年版 でも圧倒的1位を守り続けているリーダブルコードの内容の前編をわかりやすくまとめてみました。(個人的に重要だと思った箇所を抜粋)
リーダブルコードを購入しなくても、この記事さえ読めば大枠わかる程度レベルにはなっているはずです。
この記事を読むことで、より読みやすいコーディングルールの基礎を理解することが出来ます。
※ 記載されている参考コードはリーダブルコードより抜粋しています。
第1章「理解しやすいコード」
前提条件としてコードは人間が理解しやすくてはなりません。
例えば、以下のコードAとコードBではどちらの方が直感的に読みやすいでしょうか?
■ コードA
return exponent >= 0 ? mantissa * (1 << exponent) : mantissa / (1 << -exponent);
■ コードB
if (exponent >= 0) {
return mantissa * (1 << exponent);
} else {
return mantissa / (1 << -exponent);
}
コードAの方が1行にまとめられていて簡潔ですが、直感的に読みやすいのはコードBです。
読みやすいコードを判断する基準はコードを読んで他人が最短時間で理解出来るかどうかです。
コードBの方が最短時間で理解出来るのでコードBの方がより良いコードということになります。
次にこちらのコードAとコードBではどちらの方が直感的に読みやすいでしょうか?
■ コードA
assert((!(bucket = FindBucket(key))) || !bucket->IsOccupied());
■ コードB
bucket = FindBucket(key);
if (bucket != NULL) assert(!bucket->IsOccupied());
直感的に理解するまでの時間が短いのはコードBではないでしょうか?
コードAが直感的に理解しづらいのは、1行に情報を詰め過ぎているからです。
コードは短い方が良いという傾向がありますが、短くても人間が理解するのに時間がかかってしまえば本末転倒です。
人間が理解しやすいコードを常に意識することが大事です。
1章のまとめ
コードを綺麗にする時は情報をまとめすぎるより、情報を適度に分けてコードを書くほうが人間は理解しやすい。
第2章「名前に情報を詰め込む」
変数名などの名称の付け方は非常に大事です。
![スクリーンショット 2020-03-27 18.36.42.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F286094%2Fdd0339c1-f38d-9f72-b4ae-4f26ac710215.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=cae45c0089d9d33008488dc747316caa)
命名と処理に齟齬があると、上記のように想定と違う挙動になりバグに繋がるケースがあります。
命名規則は非常に重要な要素です。
例えば以下のような変数名があるとしましょう。
def getPage(url)
......
end
getPageという名称から、Pageを取得するということはわかりますが。
getという単語では情報が曖昧すぎます。
pageはどこから取得するのでしょうか?
ローカルキャッシュ?
データベース?
インターネット?
仮にインターネットから取得する場合は以下のように変数名の方が圧倒的に分かりやすいです。
def downloadPage(url)
......
end
getという単語は意味が曖昧過ぎます。
名称をつけるときは出来るかぎり明確な単語を選ぶようにしましょう。
ただ、より明確な単語にしようとすると単語が長くなるケースが多くなってしまいます。
名称の長さはどうすれば良いのか?
- スコープの小さい変数には短い名称をつけてより簡潔に(ローカルスコープ)
- スコープの大きな変数には長い名称をつけてより詳細に(グローバルスコープ)
スコープの小さい変数は影響範囲が局所的なので、変数はより簡潔につけるの方が良しとされる。
スコープの大きな変数は影響範囲が広く、処理が追いにくいのに名称はより詳細に書く方が良しとされる。
2章のまとめ
- 命名をつける時に抽象的な名前をつけるのはNG,より具体的な名称をつけるようにする
- 名称の長さはスコープの影響範囲によって使い分ける
- 具体的な名前を使って、物事を詳細に説明する
第3章「誤解されない名前」
2章の内容をもう少し深掘りします。
名称をつけるときは誤解されない名称をつけるように気をつけるべきです。
例えば以下のようなコードがあるとします。
CART_TOO_BIG_LIMIT = 10
if shopping_cart.num_items() >= CART_TOO_BIG_LIMIT: Error(" カートにある商品数が多すぎます。")
CART_TOO_BIG_LIMITという名称は非常にわかりづく、人によっては誤解を与える命名になります。
境界値を明確にするにはMAXやMINなどを名前の前につけることで意味がグッと分かりやすくなります。
MAX_CART_TOO_BIG_LIMIT = 10
他にも範囲を指定するときにはfirstやlastなどを利用することも推奨されています。
■ bad
summer_time
↓
■ good
last_summer_time
true/falseを返却する名称をつけるときは頭に is・has・can・shouldを利用することが推奨されています。
■ bad
SpaceLeft
↓
■ good
HasSpaceLeft
後、包含/排他的範囲であれば、begin と end 利用することが推奨されています。
■ bad
limit_size
↓
■ good
end_limit_size
(ページ39).
3章のまとめ
読み手に誤解を与えるような、命名は避けよう。
第4章「美しさ」
優れたソースコードは「目に優しい」ものでなければいけないです。
美しいコードを書くには一貫性のあるレイアウトを使うことが大事です。
コードAとコードBではどちらが美しいでしょうか?
■ コードA
class:class StatsKeeper {
public:
// double を記録するクラス
void Add(double d); // とすばやく統計を出すメソッド
private: int count; /* それまでの 個数
*/ public:
double Average();
private: double minimum;
list<double>
past_items
;double maximum;
};
■ コードB
// double を記録するクラス
// とすばやく統計を出すメソッド
class:class StatsKeeper {
public:
void Add(double d);
double Average();
private:
list<double> past_items;
int count; /* それまでの 個数
double maximum;
double minimum;
};
圧倒的にコードBの方が読みやすいです。
コードBでは一貫性のあるレイアウトを利用しているためストレスなくコードを読むことが出来ますが、コードAではレイアウトがバラバラで読み手にストレスを与えます。
美しいコードを書くにはコード整列が大事
コードAとコードBではどちらが美しいでしょうか?
■ コードA
details = request.POST.get('details') location = request.POST.get('location')
phone = request.POST.get('phone') email = request.POST.get('email')
url = request.POST.get('url')
■ コードB
details = request.POST.get('details')
location = request.POST.get('location')
phone = request.POST.get('phone')
email = request.POST.get('email')
url = request.POST.get('url')
コード整列したコードBの方が美しくなおかつ見やすくはないでしょうか?
さらにコード整列してコードを美しく保つとバグやタイポにも気付きやすくなります。
details = request.POST.get('details')
location = request.POST.get('location')
phone = equest.POST.get('phone') <=== equestとtypoしている
email = request.POST.get('email')
url = request.POST.get('url')
コード整形しておけばこういうミスも分かりやすく見つけることができる。
美しいコードを書くにはコードの段落が大事
コードAとコードBではどちらが美しいでしょうか?
■ コードA
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'] = friends
display['suggested_friends'] = suggested_friends
return render("suggested_friends.html", display)
■ コードB
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'] = friends
display['suggested_friends'] = suggested_friends
return render("suggested_friends.html", display)
コードBの方が圧倒的に読みやすくはないだろうか?
適度の段落とコメントを残しているので、非常に読みやすい。
そもそもコードAを見た瞬間に読みたくないと感じてしまう人も多いと思います。
4章のまとめ
- 一貫性のあるレイアウトを利用する
- コード整列をする
- コード段落を利用する
第5章「コメントすべきことを知る」
みなさんコードにコメントを書いていますか?
コメントは書き手の意図を相手に知らせる内容を書きましょう。
もう少し具体的に言うと、自分の考え方を記録するためにコメントを書くことが重要です。
コードコメントを書くときによく利用するのがアノテーションです。
例として以下の様なアノテーションがあります。
キーワード | 内容 |
---|---|
TODO | 後で追加すべき内容(機能) |
FIXME | 修正が必要な内容(機能) |
OPTIMIZE | 非効率など最適すべき内容(機能) |
HACK | リファクタリングすべき内容(機能) |
REVIEW | レビューすべき内容(機能) |
OTHER | その他、必要に応じて変更すべき内容(機能) |
個人的にTODOやFIXMEあたりはよく利用します。
アノテーションを利用することで、自分の考え方を記録するのに役に立ちます。
コメントすべき内容を考えるときに大事なのは相手の立場になってコードを読むことです。
っえ?なんでこのコード必要なの?と質問されそうな内容には積極的にコメントを書きましょう。
逆にコメントすべきではない内容は以下の様なものです。
コードから読み取れる内容
例えば以下のコメントは不要です。
// Account クラスの定義
class Account {
public:
// コンストラクタ
Account();
// profit に新しい値を設定する
void SetProfit(double profit);
};
このコードは読み手にあたらしい情報を与える訳でもなく、ただコードを読めばわかる内容を無駄にコメントしています。全く不要です。
5章のまとめ
■ コメントすべきこと
- 書き手の意図を伝えるコメント
- 読み手が疑問を解決するコメント
■ コメントすべき内容
- コードからすぐに理解できるコメント
個人的には相手の立場になってコードを読むことでコメントすべき内容が理解できると思います。
第6章「コメントは正確で簡潔に」
コメントについて、さらに深掘りしてコメントを書くコツを記載します。
簡潔に書く
// int は CategoryType。
// pair の最初の float は 'score'。
// 2 つめは 'weight'。
typedef hash_map<int, pair<float, float> > ScoreMap;
長いコメントはコード量も膨らみので、読みにくいです。
簡潔に書くと以下のように変更できます。
// CategoryType -> (score, weight)
typedef hash_map<int, pair<float, float> > ScoreMap;
コメントには代名詞は書かないようにしましょう。
■ bad
データをキャッシュする、次にそのコードをチェックする
"その"とは何さしてるのは何でしょう?
データ?
キャッシュ?
曖昧な代名詞は混乱につながります。
代名詞はやめて、できる限り詳細にコメントを書きましょう。
■ good
データをキャッシュする、次にそのキャッシュをチェックする
名前付き引数コメントを書く
例えば以下のような関数があるとします。
■ bad
Connect(10, false);
引数の10, falseは一体何を表してるのか理解できますか?
全く理解できないですよね?
そんな時は名前付き引数コメントをつけます。
■ good
Connect(timeout = 10, use_password = False)
これなら、10はタイムアウトの時間を表している、Falseはパスワードを利用しているか否かを表している。と理解することが出来ます。
6章のまとめ
- コメントは簡潔に
- 曖昧な言葉は利用しない
- 引数コメントは積極的に利用する
まとめ
とりあえず、リーダブルコードの内容の前編をまとめました。
そのうち後編も出します。
↓
後半書きました。(2020/4/12)
https://qiita.com/hirokisoccer/items/0be2c21202f0759f82ee