リーダブルコードを読んだメモ

はじめに

リーダブルコードを読んだので,印象的だった部分のメモを残します. この本は非常に読みやすく,スラスラと読めます. 読んでみて,今まで自分が書いていたコードがどれだけスマートではないのかがわかりました.そして,今後のコーディングをどのように改善すればよいか,書き方,考え方を知ることができました. ただし,読んでいるうちは「ふむふむ」と思っていても,それが実践に活かせるかというと,そこにはギャップがあると思います. 当然ですが,定着を図る必要がありそうです.

3 章 誤解されない名前

3.3 - 3.5

print integer range(start=2, stop=4)
これは[2,3]なのか[2,3,4]なのかわからない

print integer range(first=2, last=4)
これは,[2,3,4]となり,包含を表すことができる.
print integer range(begin=2, end=4)
これは,[2,3]となり,包含/排他的範囲を表すことができる.

7 章 制御フローを読みやすくする

7.2 if/else ブロックの並び順

  1. 条件は否定形よりも肯定形を使う. if (!debug)ではなく,if (debug)を使う.

  2. 単純な条件を先に書く. if と else が同じ画面に表示されるので見やすい.

  3. 関心を引く条件や目立つ条件を先に書く.

1
2
3
4
5
6
7
8
9
if (!url.HasQueryParameter("expand_all")) {
  response.Render(items);
  ....
} else {
  for (int i = 0; i< items.size(); i++;) {
    items[i].Expand();
  }
  ....
}

例における if 文では,url のパラメータとしてexpand_allを持っていない場合を,else 文ではexpand_allがある場合をそれぞれ表している. expand_allは読み手にとって関心を惹く変数および条件であるので,これを先に処理したほうが読みやすくなる.

1
2
3
4
5
6
7
8
9
if (url.HasQueryParameter("expand_all")) {
  for (int i = 0; i< items.size(); i++;) {
    items[i].Expand();
} else {
  response.Render(items);
  ....
  }
  ....
}

8 章 巨大な式を分割する

8.1 説明変数 8.2 要約変数

1
2
if line.split(':')[0].strip() == "root":
  ...

説明変数を使うと以下のようになる.

1
2
3
username = line.split(':')[0].strip()
if username == "root":
  ...
1
2
3
4
5
6
7
if (request.user.id == document.owner_id) {
  //ユーザはこの文章を編集できる
}

if (request.user.id == document.owner.id) {
  //文書は読み取り専用
}

要約変数を使うと以下のようになる.

1
2
3
4
5
6
7
8
9
final booean user_owns_document = (request.user.id == document.owner_id);

if (user_owns_document) {
  //ユーザはこの文章を編集できる
}

if (!user_owns_document) {
  //文書は読み取り専用
}

9 章 変数と読みやすさ

9.1 変数を削除する-制御フロー変数を削除する- 9.2 変数スコープを縮める 9.3 変数は一度だけ書き込む

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
boolean done = false;

while (/* 条件 */ && !done) {
  ...

  if (...) {
    done = true;
    continue;
  }

}

done のような制御フロー変数は様々な場所で設定され,プログラムに関係するデータは含まれていない.

以下のように改善できる.

1
2
3
4
5
6
while (/* 条件 */) {
  ...
  if (...) {
    break;
  }
}
1
2
3
4
5
PaymentInfo* info = database.ReadPaymentInfo();
if (info) {
  cout << "User paid: "<< info->amount() << endl;
}
...

変数 info は,関数スコープ内にあるので,またいつどこで使われるかを考えながらコードを読まなければならない.

以下のようにすることで,スコープを過ぎた info を考える必要がなくなる.

1
2
3
if (PaymentInfo* info = database.ReadPaymentInfo()) {
  cout << "User paid: "<< info->amount() << endl;
}

変数の値が何度も変更されると,コードを追いかけにくいので変数は一度だけ書き込むことを意識する. C++の const などを積極的に利用する.

10 章 無関係の下位問題を抽出する

  1. 関数やコードブロックをみて「このコードの高レベルの目標は何か?」と自問する
  2. コードの各行に対して「高レベルの目標に直接的に効果があるのか? あるいは無関係の下位問題を解決しているのか?」と自問する.
  3. 無関係の下位問題を解決しているコードが相当量あれば,それらを抽出して別の関数にする.

参考

リーダブルコード要約とリーダブルコード要約の活用方法