最初に
こんにちは!佐々木千奈です。
輪読会も3周目に突入しましたね。今回私はリーダブルコード10章と11章をまとめていきます。
今週の輪読会はリーダブルコードは第三部、コードの再構成 を進めていきます。
10章 無関係の下位問題を抽出する
関数の目的を明確にし、直接的に関係がないものは別の関数に抽出する。
無関係の下位問題を積極的に見つめて抽出する考え方
- 関数やコードブロックを見て、「このコードの高レベルの目標は何か?」と自問する。
- コードの各行に対して、「高レベルの目標に直接効果があるのか?あるいは、無関係の下位問題を解決するのか?」自問する。
- 無関係の下位問題を解決しているコードが相当量あれば、それらを抽出して別の関数にする。
例 10.3 その他の汎用コード
JSのデバッグではよく、alart()でメッセージボックスをポップアップして情報を表示させる。以下の関数呼び出しではAjaxでデータをサーバに送信して、返ってきたディクショナリを表示している。
ajax_post({
url: '<http://example.com/submit>',
data: data,
on_success: function(response_data){
var str = "{\\n";
for (var key in response_data){
str += "" + key + "=" response_data[key] + "\\";
}
alart(str + "}");
// 引き続き 'response_data' の処理
}
});
上のコードの高レベルの目標は**「サーバをAjaxで呼び出してレスポンスを処理する」である。でも、このコードの大部分は「ディクショナリをキレイに印字(pretty_print)する」**という「無関係の下位問題」を解決しようとしている。
このコードを抽出して、format_pretty(obj)のような関数にする。
var format_pretty = function(obj) {
var str = "{\\n";
for (var key in response_data){
str += "" + key + "=" response_data[key] + "\\";
}
return str + "}";
}
この関数を分離したことにより、objに期待するオブジェクト以外の文字列やundifinedなどが入った場合の例外処理なども簡単に追加することができるようになる。
その他10章メモ
- 無関係の下位問題を抽出して別の関数に抽出することで、コードが読みやすくなる上に個別にテストしたり再利用することが可能になる
- ライブラリとして実装されていないユーティリティコードは自分で作成して使いまわすのが良い👍
- 1つの機能が独立した関数となることで、関数の改善が楽になる。
- 小さな関数を作りすぎると逆に読みにくくなってしまうためやりすぎに注意。
11章 一度に1つのことを
一度に複数のことをするコードは理解しにくい。
そのため、コードは1つずつタスクを行うようにしなければいけない。
一度に1つのことを行うための手順
- コードが行っている「タスク」を全て列挙する。これは「オブジェクトが妥当か判定する」から「ツリーのすべてのノードをイテレートする」まで。
- タスクをできるだけ異なる関数に分割する。少なくとも異なる領域に分割する。
例 11.2 タスクは小さくできる
ブログに設置する投票用のウィジェットがある。ユーザーはUpとDownを投票できる。ユーザーの投票には以下のように3つの状態がある。これらの投票をscoreに反映させるためのコードを見てみよう。
ユーザーが投票や更新のためにボタンをクリックしたら以下のJavaScriptが呼び出される。
vote_changed(old_vote,new_vote); // 投票は”Up” or “Down”
もとのコード
var vote_changed = function(old_vote,new_vote){
var score = get_score();
if(new_vote !== old_vote){
if(new_vote === 'UP'){
score += (old_vote === 'Down' ? 2 : 1);
} else if (new_vote ==='Down') {
score -= (old_vote === 'Up' ? 2 : 1);
} else if (new_vote === '') {
score += (old_vote === 'Up' ? -1 : 1);
}
}
set_score(score);
};
これは短いコードだが、前の状態から次の状態への変化により、scoreへの影響を全て考えて計算しており、複雑である。複雑であるため、間違いを見つけにくい。スコアを更新するという1つのことをやっているように見えて、実際には以下の2つの処理を行っている。
- old_voteとnew_voteを数値にパースする
- scoreを更新する。
これらのタスクを別々に解決すると、読みやすいコードになる。最初の(投票を数値にパースする)という処理をvote_valueという関数として切り出した場合、以下のようなコードに改善される。
一度に1つのことを行うように改善したコード
var vote_value = function(vote){
if (vote === 'Up') {
return +1;
}
if (vote === 'Down')
return -1
}
return 0;
};
var_vote_changed = function (old_vote, new_vote) {
var score = get_score();
score -= vote_value(old_vote); // 古い値を削除する
score += vote_value(new_vote); // 新しい値を追加する
set_score(score);
};
その他11章メモ
- タスクを小さく分割し、関数の単位に分割したり、論理的な区分に分けてあげることで、コードをぐっと理解しやすくすることができる。
- コードの分割の方法は1通りではないし、複数の手法がある。正解がないが、タスクを分割して整理することが重要である。
感想
10~11章では、無関係の下位問題を抽出する、一度に1つのことを行う、という2つの視点でコードを改善する方法について学んだ。2つとも少し似た概念だと思った。どちらの場合でも、コードを一旦引いた視点から見て、リファクタリングする手法について説明していた。
コードが目的の動作を達成すると、一旦そこで満足してしまいそうになるが、そこで一度立ち止まってこれらの手法に当てはめて整理することでより読みやすいコードを作成することができ、その手間が重要だとわかった。
振り返って整理することで、ロジックが間違っていた場合などにそれに気が付くことが容易になる。
コードを書いていると、一度に複数の処理をしなければならないように感じることがあるが、タスクを様々な視点から分割する方法が紹介されていた。
同時の輪読会で龍ちゃんが12~13章のまとめを行っていたのですが、実際の経験も盛り込まれていておもしろかったです。