名著「リーダブルコード」の内容をわかりやすくまとめてみた(後編)
名著「リーダブルコード」の内容をわかりやすくまとめてみた(前編)の続きです。
この記事を読むことで、より読みやすいコーディングルールの基礎を理解することが出来ます。
※ 記載されている参考コードはリーダブルコードより抜粋しています。
第7章「制御フローを読みやすくする」
制御フローはでき限りシンプルにまとめましょう。
例えば、以下のコードAとコードBではどちらの方が直感的に読みやすいでしょうか?
■ コードA
if (a == b) {
// 第 1 のケース
} else {
// 第 2 のケース
}
■ コードB
if (a != b) {
// 第 2 のケース
} else {
// 第 1 のケース
}
コードAの方が読みやすくはないでしょうか?
コードBのような否定形の場合は一度、肯定系にしてから否定形にする作業が生じるので頭が一瞬混乱して読みにくいコードになります。
if文では肯定系をなるべく利用するようにしましょう。
また三項演算子を利用する際にも注意が必要です。
if (hour >= 12) {
time_str += "pm";
} else {
time_str += "am";
}
このコードを三項演算子に変換すると以下のようになります。
time_str += (hour >= 12) ? "pm" : "am";
確かにシンプルですが、人間が読みやすいのは前者ではないでしょうか?
if/else 文を使えば、コードがより自然になる場合は三項演算子をあえて使わないことも重要です。
(個人的には三項演算子好きなので、よく利用しますが....)
読みやすい制御フローを書くにはネストを浅くすることも大事です。
例えば以下のコードはifのなかにifを書いているのでネストが深くて読みにくい。
if (user_result == SUCCESS) {
if (permission_result != SUCCESS) {
reply.WriteErrors("error reading permissions");
reply.Done();
return;
}
reply.WriteErrors("");
} else {
reply.WriteErrors(user_result);
}
reply.Done();
最初の閉じ括弧で、「permission_result != SUCCESS」を判定して、次に「permission_result != SUCCESS」を判定しなればならない。
上記のコードのネストを2->1に変更するだけも読みやすさがグッと上がります。
if (user_result != SUCCESS) {
reply.WriteErrors(user_result);
reply.Done();
return;
}
if (permission_result != SUCCESS) {
reply.WriteErrors(permission_result);
reply.Done();
return;
}
reply.WriteErrors(""); reply.Done();
ネストが深くなると、条件が多くなるので脳内で解読するのに時間がかかる。
良いコードとはこちらでも述べたがコードを読んで他人が最短時間で理解出来るかどうかである、理解に時間がかかるコードは良いとは言えない。
7章のまとめ
- if/elseの条件に否定形を入れない
- ネストは浅くする方が読みやすい
第8章「巨大な式を分割する」
巨大な式は解読に時間がかかります。
コードを読みやすくするには処理の分割が大事になります。
コード分割には説明変数を利用します。
例えば、以下のコードを見てください。
if line.split(':')[0].strip() == "root":
これだと、一見この式が何を表しているのか解読が難しいです。
そこで、式の意味を表すusernameという変数に処理を代入します。
username = line.split(':')[0].strip()
if username == "root":
この時のusernameが説明変数です。
巨大な式を分解する
JavaScriptで書かれた、以下の巨大な式があるとします。
var update_highlight = function (message_num) {
if ($("#vote_value" + message_num).html() === "Up") {
$("#thumbs_up" + message_num).addClass("highlighted");
$("#thumbs_down" + message_num).removeClass("highlighted");
} else if ($("#vote_value" + message_num).html() === "Down") {
$("#thumbs_up" + message_num).removeClass("highlighted");
$("#thumbs_down" + message_num).addClass("highlighted");
} else {
$("#thumbs_up" + message_num).removeClass("highighted");
$("#thumbs_down" + message_num).removeClass("highlighted");
}
};
まず、式が巨大すぎて読み気が失せます。
以下の2点に注意してリファクタリングします。
① 同じコードの繰り返しを一つにする(DRYにする)
② 説明変数を利用する
var update_highlight = function (message_num) {
var thumbs_up = $("#thumbs_up" + message_num); <=== ①、②
var thumbs_down = $("#thumbs_down" + message_num); <=== ①、②
var vote_value = $("#vote_value" + message_num).html(); <=== ①、②
var hi = "highlighted"; <=== ①、②
if (vote_value === "Up") {
thumbs_up.addClass(hi);
thumbs_down.removeClass(hi);
} else if (vote_value === "Down") {
thumbs_up.removeClass(hi);
thumbs_down.addClass(hi);
} else {
thumbs_up.removeClass(hi);
thumbs_down.removeClass(hi);
}
};
読みやすさがグッとアップしました。
リファクタリング前のコードは同じコードが何度も再利用されていて、非常に読みにくかったです。
DRYにできる箇所はDRYにすることでコードがスッキリして読みやすくなります。
DRYにしたコードは説明変数などに代入することでコードの意味もわかりやすくなります。
8章のまとめ
- 巨大な式は人間が読みにくい
- DRYにすることでコードを分解する
- 分解したコードは説明変数に入れると可読性が上がる
第9章「変数と読みやすさ」
第8章では式コードを説明変数に入れることでリーダブルになると説明しましたが、変数を適当に使うと逆に読みにくいコードになってしまいます。
読みにくいコードになる、理由は以下の3点です。
1. 変数が多いと変数を追跡するのが難しくなる。
2. 変数のスコープが大きいとスコープを把握する時間が長くなる。
3. 変数が頻繁に変更されると現在の値を把握するのが難しくなる。
意味のない変数は積極的に削除することでコードが読みやすくなります。
now = datetime.datetime.now()
root_message.last_view_time = now
例えば、このコードではnowという変数に代入しているが、意味はあるのだろうか?
以下の理由からこのケースでは変数を利用する意味はないと判断できる。
- 複雑な式を分割していない。
- より明確になっていない。
- 一度しか使っていないので、重複コードの削除になっていない。
また、変数を利用する時はスコープを縮めることに意識することが大事です。
理由はスコープの影響範囲が大きくなると、どこでどのように利用しているのかが追いづらくなる。
さらに、既存の名前空間を汚染するなどの不具合に繋がる恐れがあるからです。
グローバル変数だけでなく全ての変数のスコープは狭い方がコードの影響範囲が追いやすく良いコードであると言えます。
(私が普段コードを書いている時は、ローカル変数とインスタンス変数は利用するが、クラス変数、グローバル変数は基本利用しないようにしています)
9章のまとめ
- 必要性の低い変数は利用しない
- 変数のスコープに気をつける(スコープは狭い方が良い)
第10章「無関係の下位問題を抽出する」
関数の目的を明確にすることが大事です。
直接的に関係ないものは別の関数にしてまとめよう。
以下の流れで無関係の問題を抽出して別関数にまとめる。
1. 関数やコードブロックを見て「このコードの高レベルの目標は何か?」と自問する。 (その関数で一番達成したい目的は何か?)
2. コードの各行に対して「高レベルの目標に直接的に効果があるのか? あるいは、無関係の下位問題を解決しているのか?」と自問する。
3. 無関係の下位問題を解決しているコードが相当量あれば、それらを抽出して 別の関数にする。
別関数にまとめることによって得られるメリット
1. 呼び出し側のコードはシンプルになりリーダブルなコードになる。
2. コードが独立するので、改善が楽になる
関数は小さくて独立したものになっていれば、機能追加・読みやすさの向上・エッジケースの処理などが楽にできる。
(ただ、過剰に関数を分けすぎると、あちこちに飛び回る実行パスを負わなければならないので、逆に読みにくくなるケースもあります。)
では、実際に無関係の下位問題を抽出する例をみてみます。
business = Business()
business.name = request.POST["name"]
url_path_name = business.name.lower()
url_path_name = re.sub(r"['\.]", "",
url_path_name) url_path_name = re.sub(r"[^a-z0-9]+", "-", url_path_name)
url_path_name = url_path_name.strip("-")
business.url = "/biz/" + url_path_name
business.date_created = datetime.datetime.utcnow()
business.save_to_database()
こちらのコード(phyton)はBusiness オブジェクトを作って、name・url・date_created を設定している。
このコードの高レベルの目標はbusinessオブジェクトをDB保存すること。
そして、 このコードの「無関係の下位問題」は、「名前を有効な URL に変換する」です。
この部分ですね。
url_path_name = business.name.lower()
url_path_name = re.sub(r"['\.]", "",
url_path_name) url_path_name = re.sub(r"[^a-z0-9]+", "-", url_path_name)
url_path_name = url_path_name.strip("-")
business.url = "/biz/" + url_path_name
それらを抽出して 別の関数にする。
CHARS_TO_REMOVE = re.compile(r"['\.]+")
CHARS_TO_DASH = re.compile(r"[^a-z0-9]+")
def make_url_friendly(text):
text = text.lower()
text = CHARS_TO_REMOVE.sub('', text)
text = CHARS_TO_DASH.sub('-', text)
return text.strip("-")
その結果。元のコードに「規則性」のあるパターンができ、尚且つリーダブルなコードにリファクタリングする事が出来た。
business = Business()
business.name = request.POST["name"]
business.url = "/biz/" + make_url_friendly(business.name)
business.date_created = datetime.datetime.utcnow()
business.save_to_database()
10章のまとめ
- 無関係の下位問題を抽出して関数化する事で、リーダブルで改善しやすいコードになる
第11章「一度に一つのことを」
一度に複数のことを処理するコードor関数は読みにくいです。
例えば、一度にオブジェクトを作成して、データ成形して、ビジネスロジックを適用しているようなコードです。
(的確なクラス設計がされている現場では、そういったことは少ないですが..)
複数の処理をしているコード(関数)を見つけた場合はタスクを分解しよう。
分解するときの手順はこんな感じ
1. コードが行なっているタスクを全て列挙する
2. タスクを出来る限り異なる関数に分割する
※ 詳しくは第10章「無関係の下位問題を抽出する」を参照
11章のまとめ
- コードに複数のタスクを詰め込みすぎない。
第12章「コードに想いを込める」
コードは誰でも(自分よりも技術力が低い人でも)理解できるようにすることが大事です。
誰でも理解できるようにする為のヒントを記載します。
まずロジックを明確に説明することが大事です。
例えば以下のPHPのコードにはロジックが詰まりすぎている。
(ユーザーに管理者権限があるか確認する, もし権限がなければユーザに知らせる)
$is_admin = is_admin_request();
if ($document) {
if (!$is_admin && ($document['username'] != $_SESSION['username'])) {
return not_authorized();
}
} else {
if (!$is_admin) {
return not_authorized();
}
}
まず、このコードでキーポイントになる権限ありと無しの区分を分けてみる。
するとこんな感じ
■ 権限あり
1)管理者
2)文書の所有者(文書がある場合)
■ 権限なし
3)その他
ロジックを明確に出来たので、権限ありと無しで分ける観点でコードを分割する。
if (is_admin_request()) {
// 権限あり
} elseif ($document && ($document['username'] == $_SESSION['username'])) {
// 権限あり
} else {
return not_authorized();
}
こちらの方がわかりやすい。
12章のまとめ
- ロジックを明確にして、誰でもわかりやすいコードを書くことが大事
第13章「短いコードを書く」
もっとも読みやすいコードとは何か?
それは、何も書かれないコードです。
そりゃそうですよね。
そもそもコードが書かれてないので、コードを読む必要がないので。
さらに、コード量が少なければ勿論プログラマにかかる労力も減ります。
![スクリーンショット 2020-04-12 21.30.17.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F286094%2Fcb02b687-a13d-f591-2b8a-ab5d22c5c4e5.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=3696bb4f0e5b8a10e31422ee852d5189)
画像参考:リーダブルコード(ページ170).
もしコードレスで要件機能を実現できるならそれを採用することも検討しましょう。
例えば、以下のような要望があったとします。
任意のユーザの緯度経度に対して、最も近い店舗を検索する。
この要件を実現するには以下の機能が必要と仮定します。
- 日付変更線をまたいでいるときの処理
- 北極や南極に近いときの処理
- 1 マイルあたりの経度」に対応した地球の曲率の調整
これを全てコードのみで実装するとなると相当量になります。
この要望をコードレス(もしくは少ないコード量)で実現する方法はないでしょうか?
もし、何度もインスタンスにアクセスするのであればキャッシュを利用すればコード量は減らせそうです。
他にも汎用的な「ユーティリティ」コードを作って、重複コードを削除すればコード量を少なくするとも可能です。
コードを少なく or 書かなくても実装を実現できないか自問自答することが大事です。
13章のまとめ
- コードは短い方がかかる労力が少ない
- 不要な機能は削ぎ落とす
まとめ
名著の「リーダブルコード」の内容をまとめました。
もっと詳しく知りたいという方はAmazonで購入してください。