はじめに
職業訓練を経て未経験エンジニアとして案件に取り掛かっている者です。
初学者が読むべき本としてよく上がるリーダブルコードを読みながら、要点をまとめてみました。
この記事の主な対象者
- リーダブルコードを読んだが振り返りたい人(未来の自分)
- そもそも要点だけ知りたい人
- プログラミング初学者
この記事について
全15章あるので、1章ずつが長くなりすぎないようにしたいと思います。
個人的に「なるほど」と思ったところだけを要約して記述していますので、もちろん書籍内の全ては網羅していません。
ご注意ください。
1章 理解しやすいコード
鍵となる答え
コードは他の人(未来の自分を含む)が短期間で理解できるように書かなければならない。
下記にコード例を記述↓
return exponent > 0 ? mantissa * ( 1 << exponent) : mantissa / (1 << -exponnent)
if (exponent > 0) {
return mantissa * ( 1 << exponent)
} else {
return mantissa / (1 << -exponnent)
}
どちらも処理内容は同じだが、例(2)のほうが直感的に理解しやすいと感じる人は多いのではないだろうか。
本書では、「行数が少ないことよりも、読みやすさを優先すべき」というメッセージが書かれている。
2章 名前に情報を詰め込む
鍵となる答え
名前に情報を詰め込む。
- 明確な単語を選ぶ
- メソッドの命名の例として『size()』を挙げるとすれば、size()だけでは何のサイズに関してのメソッドか分かりにくい、Height()・NumHeight()等目的に応じた命名をすべき
- 値の単位を設定する
-
const start = (new Data()).getTime();
を例とした場合、取得するのはミリ秒なので命名はstart_msとしたほうが分かりやすい
-
- 重要な属性を追加する
- 命名の例として『password』をあげると、暗号化したパスワードは『plaintext_password』と命名に属性を付けたほうがよい
- スコープが小さければ短い名前でもいい
- 小さいスコープ内で使用する変数などの命名については短くても良い(コードを理解するための情報がすぐそばにあるため)、メンバ変数やグローバル変数などでは短い名前ではいけない
3章 誤解されない名前
鍵となる答え
他の意味と間違えられることはないだろうか?と自問自答する
- 限界値を設定するときはminとmaxを使う
- カートに商品が10個までしか入らないシステムを作成する想定で
CORT_TOO_BIG_LIMIT = 10
としたとする、この場合『10未満』なのか『10以下』であるかが分かりずらい、この場合MAX_ITEMS_IN_CART
とすれば、カートに10個までしか入らないことが明確になる
- カートに商品が10個までしか入らないシステムを作成する想定で
- 暗黙的な命名は誤解の元
- filter()というメソッドを命名した場合、オブジェクトから選択するのか、一部除外するのか分かりにくい、選択するなら
select()
、除外するならexclube()
にしたほうがよい(length・limit等も同じ)
- filter()というメソッドを命名した場合、オブジェクトから選択するのか、一部除外するのか分かりにくい、選択するなら
- ユーザーの期待に注意した命名
- get()やsize()は軽量なメソッドであると期待される、重い処理を行うメソッド尾の場合、命名を変更するか軽量な処理に変更すべきである
- ブール値の名前
- 頭に『is・has・can・should』などを付けると分かりやすくなる
- 例として
spaceleft()
は数値を返すように聞こえるがbool値を返すのであれば、hasspaceleft()
のほうがよい - また、否定形より肯定系のほうが短く、読みやすい
bool disable_ssl = false // 否定形
bool use_ssl = true // 肯定形
4章 美しさ
鍵となる答え
見た目が美しいコードのほうが使いやすい、プログラミングの時間のほとんどはコードを読む時間である、さっと流し読みできるコードであればだれにとっても使いやすいコードになるだろう
- 美しいコードの心得
- 一貫性のあるレイアウト
- 一貫性のある改行位置
- {}の改行の位置を統一する
- ある場所で「A-B-C」と並べているものを別の場所で「B-A-C」等順番を変えて並べると分かりにくいコードになる
- 似ているコードは似ているように見せる
- 引数ごとで縦に同じラインで整列させる ※1
- 関連するコードをまとめてブロックにする
- 似たような処理はメソッドでまとめて処理を行う
- 宣言を多数記述するときはコメントでブロックごとに区切り宣言をする、空行を活用して段落も付けることでより分かりやすくなる ※2
- 一貫性のあるレイアウト
空欄を入れる事で引数のラインを整える
Checkprofile("Doug", "man" , 26)
Checkprofile("Doug", "" , 28)
Checkprofile("" , "woman", 26)
多数の宣言をコメントや空行で分けて分かりやすく
// ユーザー関連の情報
private String userName;
private String email;
private int age;
// ユーザー処理
public void updateUserName(String newName) {
this.userName = newName;
}
// 住所関連の情報
private String postalCode;
private String addressLine;
private String city;
// 住所関連処理
public void updateAddress(String postalCode, String addressLine, String city) {
this.postalCode = postalCode;
this.addressLine = addressLine;
this.city = city;
}
5章 コメントすべきことを知る
鍵となる答え
コメントは書き手の意図を読み手に知らせる為に記述する。
コードからすぐ分かることをコメントに書かない
下記コードはコメントが新しい情報を提供していないが、コードを理解するよりコメントを見たほうが早く理解できる。
# 2番目の'*'以降をすべて削除する
name = '*'.join(line.split('*')[:2])
また価値のない(必要ない)コメントもある、下記に例を挙げる
価値のないコメントの例
- 関数の名前と引数をそのまま文章形式で書きだしているコメント
- 例)
addUser(name, age) // nameとageをもとにuserを新たに加える
- コメントを付けるのであれば関数の役割や条件分岐を明記したほうがよい
- 例)
- ひどい名前の埋め合わせの為に付けたコメント
- 優れたコード > ひどいコード + 優れたコメント であることを意識してコメントの有無や命名を考える
- ひどいコードをコメントで補足するのではなく、コード自体を優れたものにする必要がある
また、価値のあるコメントを行う為の心得についても記述があったので下記に記述する
価値のあるコメントの例
- コメントには自分の考えを記述する
- AパターンとBパータンで比較をしてAパターンのほうが軽量な処理である場合はコメントで「Aパターンのほうが軽量であった」と記述すると読み手はBパターンについて考えなくて良くなる
- 質問されそうなことを想像する
- ハマりそうな罠を告知する
- 書籍の例としてはメソッドが外部のサービスを使っていることを挙げている
- 外部のサービスの接続に時間がかかる場合はコメントで接続に時間がかかる旨を記述したほうが親切である
- コードの欠陥をTODO:やXXX:で記述する
- ファイルやクラスの全体像についてのコメント
6章 コメントは正確で簡潔に
鍵となる答え
コメントは領域に対する情報の比率が高くなければならない
良いコメントの例がいくつか挙がっているので下記に記述する
- コメントを簡潔にしておく
- 1行で済むコメントを複数行にすることはナンセンスである
- あいまいな代名詞を避けたコメント
- 『それ』や『この』等、何を指しているのか分からないコメントを残さない
- 歯切れの悪い文章を磨く
- 関数の動作を正確に記述する
- その関数が何を処理し、どのような結果を返すのかを、はっきりと書く
- コードの意図が記述されたコメント
- この時コードの動作を書いているだけで情報を追加していないコードは良くない
- 商品を並び替える処理において
// listを逆順にイテレートする
とコメントをしても動作の説明をしているだけになる// 商品を高い順に表示する
とコメントすることで意図が伝わりやすい
7章 制御フローを読みやすくする
鍵となる答え
条件やループなどの制御フローは出来るだけ『自然』にする。コードの読み手が立ち止まったり読み返したりしないように書く
- 条件式の引数の並び順
-
if(length >= 10)
とif(10 <= length)
であれば前者が読みやすいと感じる、これは左側に調査対象の式を配置しているからである(言葉で「もし君が18歳以上なら」いうと自然であるが、「もし18歳が君の年齢以下ならば」とすると不自然であると考えると分かりやすい)
-
- ifの条件には否定形より肯定形のほうがよい。また単純な条件や関心を引く条件を先に書くことも良い(状況によって自己判断も必要になることもあるので注意)
- 三項演算子よりif/elseのほうが分かりやすい、三項演算子は簡潔になるときだけ使用
- do/whileはコードブロックを2回読むことになる可能であればwhileだけに書き換えての使用が良い
- ネストの深いコードは理解しにくいのでなるべく浅く記述する
- 書籍例を『※3』に記述します
// ネストがある処理
if(user_result == success){
if(permission_result != success){
// 処理A
return
}
// 処理B
}else{
// 処理C
return
}
// ネストを削除した処理
if(user_result != success){
// 処理C
return
}
if(permission_result != success){
// 処理A
return
}else{
// 処理B
}
8章 巨大な式を分割する
鍵となる答え
巨大な式は飲み込みやすい大きさに分割する
- 説明変数
- 複雑になった式を簡単見る為、式の一部を変数に格納する
-
if line.split(":")[0].strip() == root
よりもusername = line.split(":")[0].strip()
とif username == root
で分けたほうが分かりやすい場合もある
- 要約変数
- ifの条件内で冗長に変数を並べて比較をすると見にくくなる為、比較式自体を変数に格納して使用すると読みやすくなる
- 例として
request.user.id == document.owner_id
を条件としたifが複数ある場合、変数が5つも入っているため考えるのに時間がかかるがfinal boolean user_owns_docment = (request.user.id == document.owner_id)
として変数にすると考えやすくなる
- ド・モルガンの法則を使用してロジックを操作して読みやすいコードに変換するとよい場合がある
- 例)
if(!(a && !b))--->if(!a || b)
- 例)
9章 変数と読みやすさ
この章では課題を3つに分けている
- 変数が多いと編集を追跡するのが難しくなる
- 編集のスコープが大きいとスコープを把握することが難しくなる
- 変数が頻繁に変更されると現在の値を把握することが難しくなる
#### 課題解決に向けて以下のことが挙げられる
- 変数を削除する
-
now = datetime.datetime.now()
のように、一度しか使わない変数でかつ、意味が明確な式は、わざわざ変数に格納せずそのまま使ったほうが、コードが短くなり、読みやすくなる(課題1の解決)
-
- 変数のスコープを縮める
- グローバル変数は追跡が難しく、ローカル変数と間違えて変更してします恐れもある為、スコープを縮めてる考えは良い(課題2の解決)
- メンバ変数もミニグローバルといった考え方が出来る為、大きなクラスでは追跡や変更の把握が大変である。ミニグローバルを減らし可能であればローカル変数に変更したほうがよい(課題2の解決)
- JavaScriptにおいてconstやletで定義されていない変数はグローバル変数になる。また変数をifやforの中で定義するとコードが読みにくくなるほかエラーにつながることもある(課題2の解決)
- constやfinalを使用してイミュータブルにすれば、コードが理解しやすくなる
10章 無関係の下位問題を抽出する
- 大きな問題は小さな問題に分割してそれぞれの解決策を組み立てることで堅牢で読みやすいコードになる
- 書籍内では『与えられた地点から最も近い地点を見つける』ことを例にしており、メソッド内で『2つの地点の球面距離を算出する』ロジックが書かれていたが、全体で見るとかなり見にくいコードであった
- そこで『2つの地点の球面距離を算出する』ロジックは別でメソッドとしてまとめる事でコードが見やすくなる
- 小さい問題に分けた独立した関数は改善も比較的簡単である
11章 一度に1つのことを
- 関数は一度に1つのことを行うべき
- メソッドを分ける際はロジック内のタスクを分けて、別々の領域に分割するとよい
- 読みにくいコードがあれば、そこで行われているタスクを列挙して別の関数に分割できるものがあれば分割する
- 例として、ディクショナリから国名と町名を抽出して連結させるメソッドがあったが、名前の抽出は町名との結合とは別のタスクと考え、別のメソッドに記述を行ったことでコード全体が見やすくなった
12章 コードに思いを込める
この章ではコードの条件(やりたいこと)を人間の言葉に書き出して、条件を整理している
// 権限があるのは、以下三つ
// 1: 管理者
// 2: 文書の所有者
その他は権限はない
if (is_admin_request()){
//権限あり
elseif ($document && ($document["username"] == $_SESSION["username"])){
// 権限あり
} else {
return not_authorized();
}
言葉に書き出す以外でも問題ややりたいことを声に出して説明するだけでも効果はある
13章 短いコードを書く
プロジェクトが進んでくるとファイルが増えていき把握が大変になってくるそこで下記のことを気にする必要が出てくる
- 重複コードを削除する
- 未使用のコードや無用なコードを削除する
- プロジェクトをサブプロジェクトに分割する
- コードの『重量』を意識する。軽量で機敏にしておく
また身近なライブラリに目を通しておき、どういったときにどのようなライブラリが使用できるか把握しておくと、より簡潔にコードを記述することが出来る。
14章 テストと読みやすさ
テストコードが恐ろしく大きいものであると以下のことが考えられる
- 本物のコードを修正するのを恐れる
- 新しいコードを書いたときにテストを追加しなくなる
読みやすいテストコードは・・・
- 大切ではない詳細はユーザーから離して、大切な詳細は目立つようにする
- ヘルパー関数を使い、大切でないコードは目立たないようにする
- またテストコードを作成するときは『12章 コードに思いを込める』を参考にテストが何をしようとしているか言語化すると分かりやすい
- テストに失敗したらバグの発見や修正がしやすいようなエラーメッセージを付ける
やりすぎ
場合によってはテストに集中しすぎてしまう可能性もある、例として下記があげられる
- テストの為に本物のコードの読みや憂さを犠牲にしてしまう
- テストのカバレッジ(対象範囲に対してどれくらい網羅しているかを示す指標)が100%でないと気が済まない。
- テストがプロダクト開発の邪魔になる(テストがプロジェクト全体を支配しているような状態)
15章 分/時間カウンタを設計実装する
14章までの内容を総括して、コードを記述している章です。
命名やコメントアウトについて話がされています。
その他1つの問題の解決法が3つに分かれて説明されていますがここでは割愛します。(まだ理解しきれていない為・・・)
最後に
初めてリーダブルコードを最後まで読み、確かに素晴らしいことが書かれていると思ったが、すべてを把握して吸収できたとは感じなかった。(自分の出来の悪さが出ています)
なんとなくニュアンスでだけでもこういった感じにすればいいのかなと掴めれば、読みやすいコードに近づくことが出来るのではないかと思う。
今後も何度も読み返し、日々のコードで意識し続けたいと思います。