Robert C. Martin先生のCleanCode第3章を読んだメモ。

関数(第3章)の概要

  • 小さいこと
    • 関数内の処理は20行に達することがほとんどないように書きなさい。
    • ブロックとインデントも同じ
      • インデントレベルは1つか2つまで
  • 1つのことを行う
    • TO節で表現して1つの抽象レベルが1つの単位
    • もう一つの方法は、その関数の実装の中から単なる言い換えではなく、別の関数を抽出できるか調べる。
    • 抽象レベルを揃える。(抽象度が異なるものを混ぜない。)
      • 複数の抽象レベルのことを一緒に扱うと常に混乱を招く。
  • 逓減規則
    • コードは上から下へ物語のように読める必要がある。
    • TO節の並びとしてプログラムが読める必要がある。
  • switch文(if elseを含む)
    • 小さくするのが難しいのであんまり使わないこと。
    • 全く使わないのも難しいので、最下層の抽象Factoryに留めて、多態オブジェクトを生成し、継承の裏に隠されていれば許す。
  • 内容をよく表す名前を使う。
    • 小さな関数に内容をよく表す名前をつける。
    • 長い名前は不可解なコメントよりも優れている。
    • 名付けには十分時間をかける。
    • 整合性を持ってつける。
  • 関数の引数
    • 3つはできれば避ける。
    • 4つはよほどの理由がなければやめる。
    • 引数に現れた時点で実装詳細について知る必要が出てくる。
    • テストの観点でも、組み合わせを網羅するテストケースが必要になるため大量の引数は大変。
  • 共通モナディック形式
    • 出力引数(本書の文脈の中では、引数のオブジェクトに対して何か代入することっぽい)は避ける。
      • 混乱を招くから。
  • フラグ引数
    • booleanを引数に渡す関数は、2つ以上のことをしているからやめるべき。
  • 引数2つの関数
    • 1つの関数よりも理解しづらい。
    • 順番を覚えるのも大変
      • assertEquals(expected, actual)も、テスティングFWによって順番がまちまちで学習する必要がある。
    • new Point(0, 0) などもあるので必ずしも悪というわけではない。
    • 1つの引数に変換するための方法を知っておく必要はある。
      • メンバ変数にする
      • クラスを分ける。
  • 引数3つの関数
    • 2つよりも理解が難しい。
      • 順序
      • 立ち止まりの問題
      • 頭の中から追い出す問題
  • 引数オブジェクト
    • 引数が増えるなら、引数をオブジェクトにすれば良いじゃない。
  • 引数リスト
    • 可変引数のこと。
    • 引数が一つのList型であるものとして扱う。
    • 3つ以上の引数の中で利用してたら誤り。(例: bad(name, count, age, ...item))
  • 動詞とキーワード
    • キーワードとして引数を表すとわかりやすくなる getElement(id: string) -> getElementById(id: string)
    • 順番の問題もある程度軽減される assertExpectedEqualsActual(expected: any, actual: any)
  • 副作用を避ける
    • 1つのことを行うルールを破ることになる。
    • 出力引数は避ける。
    • 自分自身の状態を変更すべき
  • CQRS
    • 書き込み(プロパティの変更を含む)と読み込みを分ける。
    • 混乱を招くから
  • 戻りコードよりも例外を好む
    • エラーコードを返すのは微妙にCQRSに反している。
      • 呼び出し元がすぐにエラーを処理しなければいけないから。
  • try/catchブロックの分離
    • tryブロックとcatchブロックの中身を関数として外に出してしまった方が良い。
      • try/catch自体が、正常処理と以上処理が混ざって不格好だから
  • エラー処理も一つの処理
    • エラー処理以外のことをさせない。
    • tryが関数内にあるなら、関数の一番最初の単語でなければならない。
    • catchやfinallyは最後でなければならない。
  • Error.java依存性磁石
    • エラーコードを返すEnumとかやめて。
      • 依存関係が壊れやすくなる。
      • コンパイルが必要な言語ならエラーを追加するたびにビルドが必要になるから。
    • Exceptionの派生クラスをthrowして。
  • DRY
    • OOP,AOP,COP,構造化プログラミングなど様々なものが重複を取り除く試みをしている。
    • エドガー・ダイクストラの関数の出口と入口は1つにすべきというルールは関数が小さければあまり役に立たない。
    • gotoダメ絶対!
  • なぜ関数をこのように書くのか
    • コードを書くのは、執筆と同じようなもの。
    • 最初はうまく構成されていない。
    • 様々な遂行を行いテストコードを書いて洗練させる。
    • 最後に出来上がったものはこの章の規則に従ったものになる。
    • 最初から完璧なものができるわけではない。(そんなことは誰にもできない)
  • 結論
    • 話して聞かせるストーリーのように書け。

感じたこと

以前と同じく、わかりみが深い。
また、知見としてエラー処理も一つの処理としてtryが最初でcatchが最後とかは、結構悩んでいたポイントなので目からウロコだった。
あと、直近でError.java依存性磁石は抱えていた問題だったので、なるほど例外クラスの派生クラスで良いのかというポイントも目からウロコだった。

DRYに関しては、かなり難しい。
同著者のCleanArchitectureでも記載されているように 偶然の一致一致 を見分けるのがかなりセンスが必要になるなと感じた。

あと、コンストラクタの引数が結構増えがちだが、これもオブジェクトにして少なくすべきなのだろうか。。。
少なくすることは、技術的には可能だが、結局ネストが増えてデメテルの法則を破りそうでかなり難しいと感じた。