雑多な日記

雑多である

リーダブルコードを読んだ

かの有名なリーダブルコードを読んだ。 読書感想文を書いてみる。

書籍情報

タイトル:リーダブルコード - より良いコードを書くためのシンプルで実践的なテクニック
著者:Dustin Boswell, Trevor Foucher
訳:角征典
発行:オライリージャパン

概要

大きく3つのテーマで構成される

  • 表面上の改善
    • 命名やコメントなどコードのロジックに関わらない部分での改善
  • ループとロジックの単純化
    • ループのネストの回避、巨大な式の分割など
  • コードの再構成
    • 目的とする処理をどのように分割して読みやすいコードに落とし込むか

訳者前書きでも書かれている通り、当たり前と言えば当たり前の内容。しかし非常に重要な内容だった。

表面上の改善

命名

  • 明確な単語を選ぶ
    • 変数や関数を表すのに必要十分な意味を持った単語を組み合わせる
    • get, setは特に軽量なアクセサとして使用されるのが常識となっている。他の用途に使用しない。
  • 汎用的な名前を避ける
    • tempみたいな一時変数であること以外なんの情報もない名前は避ける。ましてやhogeとかコミットするのはもってのほかである(1敗)。
    • スコープがごく小さく、生存期間の短い、まさに一時的な値の保存と言う場合は例外的にtempを使用する。
    • ループイテレータi, j, kのままでよい
  • 抽象的な名前よりも具体的で直接的な名前をつける
  • 名前のフォーマット
    • キャメルケースやケバブケース等プロジェクト内で統一する
  • 誤解されない名前
    • 実際の処理と乖離があり、誤解されるような命名は避ける
    • 範囲指定
      • max/min
      • first/last
      • begin/end
    • bool値の先頭にはis, has, can, shouldを使用する
    • 否定系のbool値は紛らわしいため避ける
    • 計算コストの大きなメソッドにはそうとわかるような命名をする
    • 命名の際には複数の候補を挙げて検討する

レイアウト

  • 一貫性は読みやすさ
  • 似ているコードは似ているように見せる
  • 関連するコードをまとめてブロックを作る
  • ヘルパーメソッド
    • 重複を排除する
    • 視覚的に見やすくする
    • 変更や追加を容易にする

コメント

  • コメントの目的は読み手に書き手の意図を伝えること
  • コメントすべきではないこと
    • コードからすぐさまわかること
    • 間違った命名への注釈
      • コメントする前に間違った命名を訂正する
  • 優れたコード > ひどいコード + 優れたコメント > コメントなしウンコード
  • コードの欠陥にコメントをつける
コード 意味
TODO あとで手をつける
FIXME 既知の不具合があるコード
HACK あまりきれいではない解決策
XXX 大きな問題のある危険なコード
  • 定数にコメントをつける
    • 値がどれくらいで妥当なのか、どういう意図でその値にしているのか
  • 質問される前にコメントをつける
  • 誤用される危険がある箇所はコメントで警告する
  • 全体像についてコメントする
    • <- コード内より別のドキュメントの方が良くない?
  • コメントは正確で簡潔に

ループとロジックの単純化

制御フロー

  • 条件式の引数の並び順
    • 条件式の左側:調査対象の式
    • 条件式の右側:比較対象の式
  • if/elseブロックの並び順
    • 否定系よりも肯定系を使う
    • 単純な条件を先に書く
    • 目立つ条件を先に書く
  • 三項演算子
    • 使い方によっては読みやすくもなるし読みづらくもなる
    • 使用することによってロジックが簡潔になる場合のみ使用する
  • do/while -> while
    • ループの終了条件は初めに示す方が良いよね
  • ガード節
    • returnを使って関数から早く返す
    • クリーンアップコードについてはusingなどを使用する
    • ループではcontinue
  • goto反対!!!
  • ネストは浅くする
  • コードを追いづらくしがちな要素
    • スレッド
    • シグナル/割り込みハンドラ
    • 例外
    • 関数ポインタと無名関数
    • 仮想メソッド

巨大な式の分割

  • 説明変数/要約変数を使用して分割する
    • 要約変数はコードの重複の排除にも役立つ
  • ド・モルガンの法則を使用して複雑な論理式を投下で簡潔な式に変換する
  • 短絡評価を使用した複雑なコードは読みづらい
    • if(a || b)atrueだとbは評価されない
assert((!isExistHoge()) || (applyHoge() != expectedResult))
  • マクロを定義する
  • 無駄に繰り返さない(DRYの原則)

変数と読みやすさ

  • 不要な変数を削除する
    • 複雑な式を分割していない
    • 使用することでより明確に説明的になっているわけではない
    • 重複コードの削除に貢献していない
    • 単に中間結果を保持しているだけ
    • 制御フロー変数
  • 変数のスコープを必要最低限のレベルにする

コードの再構成

下位問題の抽出

  • コードの高レベルの目標が何かを定める
    • この目標を基準として下位の問題をどんどん切り離して、汎用コードに落とし込む
  • インターフェースを整える
    • インターフェースの汚いライブラリがあれば自分でラッパ関数を作って整える
  • やりすぎもよくない

一度に一つのことをする

  • 複数のタスクを一度に行うように記述すると読みづらい
  • 関数に分離するかコードブロックを分ける
  • リファクタリングする際はまず、そのコードで行われているタスクを列挙する。そしてそのタスク単位で分割する。
    • 問題の整理にはまず簡単な言葉で説明しようとするのが有効である(ラバーダッキング:テディベアに問題を言葉で説明してみることが問題の解決につながったりする)

コードは小さく保つべき

  • プログラマはプロジェクトに欠かせない機能を過剰に見積もり勝ち
  • プログラマは実装にかかるコストを過少に見積もりがち
  • 真に要求されるものを見つけて、より単純な形で解決する
  • コードを小さく保つために行うこと
    • 汎用的なコードを分離して重複をなくす
    • 未使用のコードや無用の機能を削除する
    • プロジェクトをサブプロジェクトに分割する
    • コードの重量を意識する
  • ライブラリの利用
    • ライブラリで実現できることをわざわざ自前で実装するのは無駄の極み

おまけ

テスト

  • テストは読みやすく、保守しやすいものでなくてはならない
    • テストが汚いとコードの修正するコストが上がり、テストが保守されなくなる
  • 最小単位のテストの集合体にする
  • エラーメッセージは読みやすく
  • テストケースを適切に設計する
  • テストの命名
    • そう頻繁に呼び出されるものでもないので、長くてもよい。説明的で何のテストなのかわかりやすい命名をする。
  • テストを意識した開発
  • テストが難しい設計(アンチパターン)
  • テストが容易な設計
  • テストがコードやプロジェクトの品質を落とすことがないようにする

付録

書籍の紹介

ロバート・C・マーティンの本は何かしら読んでおきたい

解説

読者がこれから行うべきこと

  • 実際にやる
  • 当たり前にする
  • コードで伝える

それはそう。
全人類きれいなコードを書こう。

感想

それはみんな読めって言うよな〜って内容だった。プロジェクトに関わる人間みんながこの内容を遵守してたら、コードはめっちゃくちゃきれいに保たれて、関わる人間みんなハッピー世界平和万歳って感じだ。読みやすいコードとはどういうものか、それを保つために必要なテクニックについてわかりやすく簡潔に記述されている。とても簡潔でページ数は薄い。この薄さでこの値段か〜という気持ちは正直あるけど、まあナイスな内容やったしいっかという感じ。

命名に関して、弊社でよくみるハンガリアン記法。あれどうなんだろうな。あまり好きじゃないけどみんな使ってるならそれでもいいかくらいの印象。弊社ではbIsOkとかiRetValみたいなのそこらじゅうに転がってる。ちゃんと統一するならあれはあれでいいかなと思うけど、問題はノットハンガリアンも相当数いること。本書でも触れられてたけど一貫性のないコードはゴミなんだよな〜。後から入ってきたわいはどっちに倣えばええねん。ハンガリアンは置いといたとしてもbIsOkiRetValは駆逐するべきだよな。もう少し情報くれ。10行程度の関数で使うんならいいけど、君ら数百行の関数で出てくるもんな。数百行の関数自体も消えて欲しいが。

後半感想じゃなくて愚痴だ。ええねん。わいはデスクにリファクタリングを置いて周囲を威嚇するんや。

おわり