読者です 読者をやめる 読者になる 読者になる

グループワークに向けて(3)

今回の目的

今回はクループワークに向けて(2)で扱った「リーダブルコード」の続きを勉強していきます。 今回はコードの内部、ロジカルなところを読みやすくするなどのことがメインです。第Ⅲ部まで進んでしまいましょう。

第Ⅱ部 『ループとロジックの単純化』

複雑なループ、巨大な式、膨大な変数を見ると、真剣に考えて記憶しなければいけないので、頭のなかに「精神的な荷物」が増えてしまう。 これは「理解しやすい」とは正反対のことだ。 コードに「精神的な荷物」がたくさんあると、バグは見つからなくなるし、コードは変更しにくくなるし、何よりコードに触れるのが楽しくなくなる。

(「リーダブルコード」第Ⅱ部より)

ここではループやロジックを簡単にし、読み手に理解してもらいやすいコードを書くにはどうすれば良いのかについて言及しています。

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

  • 条件式の左に変化する調査対象、右にはあまり変化しない比較対象を置くようにする
  • if/else文の書き方
    • 条件は否定型よりも肯定型を使う
    • 単純な条件を先に書く
    • 関心を引く条件や目立つ条件を先に書く
    • この優劣が衝突した場合は、自分で優先度をつける
  • コードを短くするよりも、読み手の理解する時間を短くする
    • 三項演算子はif文で書くよりもわかりやすい時のみ使う
      • ex) time_str += (hour >= 12) ? "pm" : "am"
    • 関数内では早めにreturnする( = ガード節)
    • do/while文やgoto文は使わない(コードが飛び飛びになってしまう)
  • ネスト(二重ループや二重if文など)を浅くする
    • コードを書き直すときはコードを新鮮な目で見る、一歩下がって全体を見る
    • returncontinueを使って早めに返す
実行の流れを追いづらくする構成要素 高レベルの流れが不明瞭になる理由
スレッド どのコードがいつ実行されるのかわからない
シグナル/割り込みハンドラ 他のコードが実行される可能性がある
例外 色々な関数呼び出しが終了する可能性がある
関数ポインタと無名関数 コンパイル時に判別できないので、
どのコードが実行されるかわからない
仮想メソッド object.virtualMethod()は未知の
サブクラスのコードを呼び出す可能性がある

巨大な式を分割する

  • 説明変数:式を表す変数を作り、何を指し示すのかを明確にする
  • 要約変数:条件式などを代入し、四季の意味を要約する
  • ド・モルガンの法則を使う
    • notを分配/括りだし、andorを反転させる
  • きれいに書けた(頭がいい)コードではなく、読みやすい(簡潔な)コードを書く
    • コードは長くなってもいいのでわかりやすく分割しよう
    • イディオムを知っておくと頭がいい簡潔なコードが書ける場合がある
  • コードを書くのが難しく、長くなってしまいそうな場合は逆転の発想をしてみる。
  • 巨大な文に含まれる同じ式を変数に置き換える
    • 見やすくなり、スペルミスがなくなる
    • 変更しやすくなる
  • よく似ているが、同じでないものはマクロや関数を組んで見やすくする

変数と読みやすさ

  • 不要な変数を削除する
    • 役に立たない変数、なくても理解に支障のない一時変数
    • 中間結果を保持するだけの変数
    • 制御フロー変数(ループを制御するフラグ)
      • breakを代わりに使おう
  • 変数のスコープ(有効範囲)を縮める
    • グローバル変数をローカル変数に「格下げ」する
    • メソッドをできるだけstaticにする
    • 大きなクラスを小さなクラスに分割する
    • 言語によっては条件式の中で変数を宣言できるのでそれを使う
      • ex) C++ : if(PaymentInfo* info = database.ReadPaymentInfo()){...}
      • ex) java : for(int i = 0; i < 10; i++){...}
    • JavaScriptpythonなどの変数宣言を必要としない言語では意図しないグローバル変数を生み出しやすい
      • タスクをできるだけ早く完了させる
      • 変数をグローバルにし、Noneなどを代入しておく
    • 変数の宣言を変数の使用直前に置く
  • 変数は一度だけ書き込む
    • constfinalなどを使おう

まとめ

プログラミングの技術的な部分に大きく関わる内容でした。 とても細かく、第Ⅰ部と違って強く意識しなければ身につかない内容なので、何度もこの本やこのページを振り返って確認しながらコードを書いていきましょう。

第Ⅲ部 『コードの再構成』

コードを理解しやすくするには、コードを書かないのがいちばんだ。

(「リーダブルコード」第Ⅲ部より)

第Ⅲ部にはコードを大きく変更する方法について書かれています。 コードを再構成したり完全に削除することで、さらに読みやすいコードにしていきましょう。

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

1. 関数やコードブロックを見て「このコードの高レベルの目標は何か?」と自問する
2. コードの各行に対して「高レベルの目標に直接効果があるか?あるいは無関係の下位問題を抽出しているか?」と自問する
3. 無関係の下位問題を解決しているコードが相当量あれば、それらを抽出して別の関数にする
  • 無関係の下位問題を積極的に見つけて抽出する(別の関数やクラスを作る)
    • コード量の多いループ
    • 将来的に再利用可能な部分
    • 完全に自己完結しているコード
    • 有用だが実装されていない関数
    • 無関係の下位問題を処理しているコード
  • コードを抽出しておくと、改善が楽になる
  • プロジェクトから切り離された汎用コードをたくさん作る
  • 完全に独立していなくても、取り除くだけで読みやすくなる
  • インターフェイスを直感的に扱いやすく変更する
    • プログラムの本質的なロジックを扱うコードを簡潔にする
    • 「理想とは程遠いインターフェイスに妥協することはない」
  • やりすぎてしまうと可読性が著しく下がってしまう

一度に1つのことを

  • 一度に1つのタスクをする
    • プロジェクトのタスクを列挙する(小さくても緩くてもいい)
    • タスクを出来るだけ異なる関数に分割する
    • 分割出来ない場合は少なくとも領域に分割する
  • 「タスクをどのように分割するか」より、「分割すること」自体が重要

詳しい例については本に記載されているのでそちらを読むと理解が深まります。

コードに思いを込める

おばあちゃんがわかるように説明できなければ、本当に理解したとは言えない。

(アルバート・アインシュタイン)

  1. コードの動作を簡単な言葉で同僚にもわかるように説明する
  2. その説明の中で使っているキーワードやフレーズに注目する
  3. その説明に合わせてコードを書く

プログラムのことを簡単な言葉で説明することで、コードをより自然なものに書き換えることが出来る。(ラバーダッキング) 問題や設計を上手く説明できないならば、何かを見落としているか、詳細が明確になっていないということだ。 プログラム(あるいは自分の考え)を言葉にすることで明確な形に成るのである。

(「リーダブルコード」第Ⅲ部 12章 まとめより)

短いコードを書く

  • 不要な機能は実装しない
    • プログラマはプロジェクトに欠かせない機能を過剰に見積もりすぎ
  • 機能に必要な精度は、利用先によって大きく変化する
    • すべてのプログラムが高速で、100%正確で、あらゆる入力を上手く処理する必要はない
  • コードを出来るだけ小さく軽量に維持する
    • 汎用的で有用なコードを作り、重複コードを削除する
    • 未使用のコードや無能なコードを削除する
    • プロジェクトをサブプロジェクトに分割する
    • コードの「重量」を意識する(軽微で機敏にしておく)
  • 身近なライブラリに親しむ
    • APIを知っていれば容易に解決してしまうことがある

まとめ

長く単調になってしまいましたが、これが第Ⅱ部と第Ⅲ部についての内容の大まかなものです。 とても重要なことが多く、本書を実際に読んで理解を深めていただきたいです。

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

次回は今回の内容を実践し、第Ⅳ部の内容についてお話します。