# リーダブルコード 第Ⅰ部 ## 1章 理解しやすいコード ### 1.1 優れたコードって何? プログラマーはなんとなく直感でプログラミングのことを決めていることが多い。 だが、「簡潔」なコードと「安心」なコード、どちらが大切? ### 1.2〜1.5 読みやすさの基本定理 **コードは他の人が最短時間で理解できるように書かねばならない。** ここでの「理解する」は、変更やバグ発見が可能になるレベルでの理解である。 簡潔で短いコードは、理解しにくい恐れがある。 コメントを追加するなどして、コード量よりも理解の時間を最小限にする必要がある。 理解の時間を短くすることは、他の条件とは競合しない。 **「このコードは理解しやすいだろうか?」と自問自答してみる**ことが大切である。 # 第I部 表面上の改善 ## 2章 名前に情報を詰め込む プログラム上で命名する時は、**情報が伝わる名前にする。** 短くても有意義な名前をつければ、多くの情報が伝わる。 ### 2.1 明確な単語を選ぶ get, size, number等、抽象的・全体的な意味を持つ単語1つでは、情報は伝わらない。 (何を得るの?、何のサイズ?、何の数字?:対象が何か不明) **より意味を絞った単語、あるいは2語以上を用いて情報を深める必要がある。** 類語辞典などを用いて適切な単語を調べておくのも一手である。 * get: getFirstName, gain等 * size: memoryBytes, height等 * number: customerNumber, sum等 * stop: kill(取り消し不可を強調), pause(リジューム可能を強調)等 ### 2.2 汎用的な名前を避ける tmp, reveral, foo等の**汎用的な変数名は、それ相応の理由での使用以外は避けた方が良い。** 名前が具体的になると式との矛盾も見つけやすい。 ### reveral reveralは返り値の意味で、それ以外の意味は無い。returnの処理や値に応じて具体的な名前を!(sum_squares:2乗の合計) #### tmp tmpは、変数に役割が無いことを意味する。したがって2変数の入れ替え時の一時退避変数など、生存期間が短く一時的な場合には有効である。 但し、こうした本来の役割以外の使い方は避けるべきである。 #### ループイテレーター while文やfor文のループやインデックス等、繰り返しの中で用いる一時的な変数名(i, j, k 等)のこと。 イテレーター以外の意味での利用は避ける。 イテレーターが複数ある場合は、club_i, members_i, users_i(あるいはci, mi, ui)のように説明を付け加えると良い。 ### 2.3 抽象的な名前よりも具体的な名前を使う **具体性のない抽象的な名前は避ける。** 何を意味するのかがはっきりわかる名前を! #### 抽象的な名前 * ServerCanStart(具体的な動作は?) * DISALLOW_EVIL_CONSTRUCTORS(許可しないコンストラクタはどのコンストラクタ?) * --run_rocally(ローカルでテスト内容を印字したい→リモートでの印字やローカルでの不使用:環境と動作が一致しない) #### 具体的な名前 * CanListenOnPort(ポートをリッスン可能に) * DISALLOW_COPY_AND_ASSIGN(コピーと承諾のコンストラクタを拒否) * --use_local_database(ローカルのデータベースを用いる) ### 2.4 名前に情報を追加する **大切な情報は名前に込めるべきである。** 変数名に単語を追加すると良い。 * id → hex_id(hex:16進数) * start, elapsed → start_ms, elapsed_ms(elapse:経過, 単位(ms)をつけると変数の属性(例では速さ(m/秒))が明確になる。) * url, MessageBody, password, html → untrustUrl, unsafeMessageBody, plaintext_password, html_utf8(セキュリティに関する情報や、文字コードや文字形式等の属性情報は、名前で示す方が良い) ### 2.5 名前の長さを決める 情報を詰め込みすぎて何単語も積み重なるような名前は、コードが長くなり画面を占領するだけなので良くない。 大抵の場合、多くとも3~4単語あれば伝わる。 スコープが小さい時は短くても差し支えないが、識別子のスコープが大きい場合は具体的な名前にする必要がある。 エディタには単語補完機能が備えられており、最初の1文字を入力して機能を実行すると、単語が自動で補完される。 正しい文字が出るまでコマンドを繰り返せば、正確な単語入力が可能となる。 短縮表記(Back End Manager→BEManager, String→Str 等)は、チームメンバーが理解できないような使い方は避ける。 また、不要な情報となる単語は切り落としても良い。(ConvertToString()→ToString()など) ### 2.6 フォーマットで情報を伝える 大文字小文字の区別やアンダースコア、ダッシュ等のフォーマットを用いて情報を伝える。 フォーマット規約は言語によって異なるため、言語に応じて適切なものを使い分ける。 **プロジェクトで一貫性を保つことが大切である。** ## 3章 誤解されない名前 複数の意味合いを持った単語や、曖昧な区別を表す単語など、**誤解されてしまう恐れのある単語の利用は避ける。** ### 3.1 filter(引数) filterは区別が曖昧で、引数以外にフィルターをして引数だけを取り出すのか、逆に引数にフィルターをして引数以外を取り出すのか区別がつかない。 引数を取り出す場合はselect、引数以外を取り出す場合はexcludeが適切である。 ### 3.2 Clip(text,length) Clip()は第一引数のtext部分の末尾から第二引数lengthだけ削除する関数である。 ここで問題となるのがlengthの扱いである。以下の2通りが考えられる。 * length=文字数 末尾からlengthの長さ(文字数)だけ削除する。 * length=最大値 最大lengthの長さ(文字数)だけ削除する。 対処法として、lengthという単語の範囲が曖昧なため、最大値や最小値を表すときはmax_length, min_lengthなどとした方が良い。(3.3も参照) また、lengthという単語自体をChars(文字数)などの具体性のある言葉に置き換えるとよい。 ### 3.3 限界値:min, max 限界値を表すにはmax_及びmin_を名前の先頭につける。 こうすることでどこまでが含まれるのかが明確になり、範囲の区別(未満、以下、以上、超)も明確になる。 ### 3.4 範囲指定:first, last start/stopで範囲指定する場合、stopでは限界値の扱いがわからない。 first/lastで範囲指定すれば、lastは必ず範囲に含まれることになる。 ### 3.5 包含/排他的範囲:begin, end 最初の指定数値は含まれ、最後の指定数値は含まれない場合、begin/endを用いる。 beginの指定値は含まれるが、endの指定値は含まれない。 ### 3.6 boolean型の変数名 boolean型の場合、同じ単語で意味が異なる場合(read:現在形と捉えてこれから読み込む or 過去形と捉えてもう読み込んだ)は判定条件が異なってしまう。 こうした場合はis, has, can, shouldなどの助動詞を名前につけると良い。また、否定形の使用は避ける。 ### 3.7 ユーザの期待に合わせる ユーザの先入観による誤解が生まれる恐れもある。get*()は、全ての過去のデータをイテレートする仕様であれば、意図に反して膨大な時間がかかってしまう恐れがある。また、list.Size()では、計算量がO(n)であると知らずにリスト全体の計算量がO(n^2)となり、こちらもデータ数が膨大だと指数関数的に処理時間が増大してしまう。名前が故に混乱を招きやすい状況になるのは避け、ユーザの期待に合わせる。 ### 3.8 複数の名前を検討する 名前を決定する場合、複数の候補を提示し検討する。 それぞれの長所及び誤解を招く要素を検討した上で、どのような工夫を施した名前にすべきかを検討する。 ## 4章 美しさ ### 4.1 なぜ美しさが大切なのか **優れたコードは「目に優しい」ものでなければならない。** 見た目が美しいコードが大切なのは、**見やすくかつ使いやすいため**である。 #### 3つの原則 * **読み手が慣れているパターンと一貫性のあるレイアウトを用いる。** * **似ているコードは似ているように見せる。** * **関連するコードをまとめてブロックにする。** ### 4.2 一貫性のある簡潔な改行位置 長い一文を改行する場合、その位置は他の同様な要素と一貫させて揃えると見やすくなる。 また、同一の意味を表す(引数と変数名だけが異なる)ものを複数記入する場合は、あらかじめ汎用的なメソッド形式にしておき、 それをnewする形で変数名別に引数を設定しておくと、簡潔になる。 ### 4.3 メソッドを使った整列 関数が長く、綺麗なコードの妨げとなってしまっている場合、メソッドを用いて関数部分のみを切り取るとスッキリする。 また、複数同一メソッドがある場合は、属性などを汎用的なメソッドにして重複を削除するとより簡潔にまとまる。 また名前やエラー文字列等の重要部分が見やすくなり、テストの追加も容易になる。 ### 4.4 縦の線をまっすぐにする 変数定義のイコールの位置や引数の区切り(コンマ)の位置等を、複数の項目で揃えることで分かりやすくなる。 ### 4.5 一貫性と意味のある並び 変数定義の順番など、コードの正しさに影響を与えない部分に対しても、フォームの入力順、重要度の高い順、アルファベット順等で揃えると良い。 但し、一貫性を維持するため、一連のコードでは同じ並び順を用いる必要がある。 ### 4.6 宣言をブロックにまとめる 同じ意味を表す論理的なグループにまとめておくことで、概要が把握しやすくなる。(getterだけを集める、setterだけを集める等) ### 4.7 コードを「段落」に分割する コードを改行なくつらつらと書くのではなく、処理の意味ごとにコメントアウトし、処理を段落に分割する。 ### 4.8 個人的な好みと一貫性 個人的な好み(中カッコの始点の位置)等は、どちらでも処理場の影響は無いが、常に自分のスタイルに合わせる必要がある。 一貫性のあるスタイルは、**「正しい」スタイルよりも大切である。** ## 5章 コメントすべきことを知る ### 5.1 コメントすべきでは「ない」こと コードからすぐに分かるような説明(コンストラクタ、新しい値を設定する、profitを返す…等)は**書くべきではない。** 宣言よりも前に、関数や引数の名前を文章でコメントするような、コメントのためのコメントは避ける。 また、名前の意味を汲むためのコメントは避け、名前を変更する。変更は「自己文書化」された名前を用いる。 ### 5.2 自分の考えを記録する コメントをつけるべき場面の一つは、実際のテスト結果に基づく指摘や、コードが汚いのでこう改善した方が良い等のアドバイスを与える場合である。 また、定数の値に対しコメントする場合にも用いる。(例:NUM_THREADS=8 //なんで8? 値は5あれば十分) 欠陥にコメントする場合は、以下の省略コードを先頭に記入すると、読み手側が理解しやすくなる。 * TODO:(意味:後で修正してください。) * FIXME:(意味:不具合があります。) * HACK:(意味:あまり良くない解決策です。) * XXX:(意味:危険なコード!直ちに修正を。) ### 5.3 読み手の立場になって考える #### 読み手の疑問に答える 作り手ではなく読み手の立場になって考えたとき、なぜこの処理が必要なのか?等の疑問点が生じる可能性がある。 **読み手の立場に立って、どんな疑問を抱くかを予め想像しておき、** その疑問の答えとなるコメントを残すと良い。 #### 陥りやすい罠を告知する 仕様上陥りやすいミスを防ぐために、誤った使い方を防ぐためのコメントを残す工夫も必要である。 #### 全体像へのコメント システムの全体の処理の流れを把握していない人にも理解できるよう、**どのような処理を行うための機能であるか等の説明書き**が必要である。 初見の読み手でもシステムがどのように機能しているのかが分かるようにしておく。 #### 要約コメント 全体像へのコメントを、関数単位で付けたもの。 コードを見たらわかるような1つの処理自体を説明するコメントではなく、**関数全体のプロセスでどのような処理が行われているか**をコメントする。 ### 5.4 ライターズブロックを乗り越える コメントを書き出すのは大変だと思っているプログラマーは多い。 こうした「ライターズブロック」を乗り越えるべく、まずは思うままに自分の考えを書き出してみると良い。 書き出してみた上で、綺麗な言葉で清書してあげればよい。 ポイントは、XPの考え方と同様に「**早めに、こまめに**」コメントアウトすることである。 ## 6章 コメントのブラッシュアップ コメントは **「正確でかつ簡潔」でなければならない。** また、その**領域に対する情報の比率が高くなければならない。** ### 6.1 コメントを簡潔にする 1行で収まるなら余計な行数を取る必要はない。 ### 6.2 あいまいな代名詞を避ける これ、それ、あれ、この、その、あの…といった代名詞は極力避ける。 ### 6.3 歯切れの悪い文章を磨く どっちつかずの文面を避け、具体的に明示する。 正確な情報は短いコメントにもつながる。 ### 6.4 関数の動作を正確に記述する 言葉の扱う範囲の範囲が大きく具体性がない言葉は避け、正確な情報が伝わるようにする。 ### 6.5 入出力のコーナーケースにに実例を用いる 入出力処理の場合、具体例を取り上げてどのような処理が返ってくるのかを明確にする。 但し、全てのパターンを網羅できるようにすること。 ### 6.6 コードの意図を書く コードの処理内容を説明するのではなく、動作によってもたらされる結果、すなわちコードの意図を説明する。 必要のない処理の場合、コメントが冗長検査の役割を果たす。 ### 6.7 「名前付き引数」コメント 良く分からない引数を説明する場合、「名前付き引数」を用いる。 JavaやC言語等の場合、インラインコメントを用いることで名前付き引数を表現できる。 ### 6.8 情報密度の高い言葉を使う 説明が長くなる時は、一言で情報密度の高い意味を表す言葉を用いる。 (例:正規化、ヒューリスティック、ブルートフォース等) ###### tags: `読書`