「良いコード/悪いコードで学ぶ設計入門」読書メモ

話題の「ミノ駆動本」を早速読んでみた。自分のindex用雑なメモ。

感想

  • 初中級者向けとはいえ、粗が多い自分には結構学びが多かった。これまで特にアプリケーションのプロダクトコードでこれが実践できているようなコードを見ることは稀なので、その領域のほとんどのエンジニアは読むべき本だと思った。
  • 初級エンジニアのレビューで良く指摘するような基本的な考え方は、実例とともにわかりやすく書いてあるので、そういった方には是非一読してほしい内容。
  • 15章でこの本が品質特性のうち保守性、特に修正性==変更容易性について書かれたものであることを説明している。最初にこの章をざっと読んでも良いかもしれない。
  • 個人的には初級のうちからテストを書くことを当たり前にしてほしいので、テストの重要性や修正性の高いテストコードの考え方についてあまり説かれていないのがちょっと残念

  • インスタンス変数をイミュータブルにして、変更があるときは新たなオブジェクトを返す考え方は、関数型などの最近の考え方っぽく学びがあった
  • DDDでも出てくる「値オブジェクト」もっと積極的に取り入れようと思った
  1. 第1章 悪しき構造の弊害を知覚する
  2. 第2章 設計の初歩
  3. 第3章 クラス設計 – すべてにつながる設計の基盤 –
  4. 第4章 不変の活用 – 安定動作を構築する –
  5. 第5章 低凝集 – バラバラになったモノたち -感想
      1. 5.1.3 インスタンスメソッドのフリしたstaticメソッドに注意
      2. 5.1.5 どういうときにstaticメソッドを使えばいいのか
      3. 5.2.1 privateコンストラクタ+ファクトリメソッドで目的別初期化
      4. 5.2.2 生成ロジックが増えすぎたらファクトリクラスを検討すること
    1. 5.3 共通処理クラス(Common・Util)
      1. 5.3.1 さまざまなロジックが雑多に置かれがち
      2. 5.3.2 オブジェクト指向設計の基本に立ち返ろう
      3. 5.3.3 横断的関心事
    2. 5.4 結果を消すために引数を使わないこと
    3. 5.5 多すぎる引数
      1. 5.5.1 プリミティブ型執着
      2. 5.5.2 意味のある単位ごとにクラス化する
    4. 5.6 メソッドチェイン
      1. 5.6.1 尋ねるな、目白命じろ
  6. 第6章 条件分岐 – 迷宮化した分岐処理を解きほぐす技法 –
      1. 6.3.1 ポリシーパターンで条件を集約する
  7. 7章 コレクション – ネストを解消する構造化技法 –
  8. 8章 密結合 – 絡まって解きほぐせない構造 –
  9. 9章 設計の健全性をそこなうさまざまな悪魔たち
  10. 10章 名前設計 – あるべき構造を見破る名前 –
  11. 11章 コメント – 保守と変更の正確性を高める書き方 –
  12. 12章 メソッド(関数) – 良きクラスには良きメソッドあり –
  13. 13章 モデリング – クラス設計の土台 –
  14. 14章 リファクタリング – 既存コードを成長に導く技 –
  15. 15章 設計の意義と設計への向き合い方
  16. 16章 設計を妨げる開発プロセスとの戦い
  17. 17章 設計技術の理解の深め方
  18. リンク
  19. 取り入れたいこと

第1章 悪しき構造の弊害を知覚する

第2章 設計の初歩

第3章 クラス設計 – すべてにつながる設計の基盤 –

第4章 不変の活用 – 安定動作を構築する –

第5章 低凝集 – バラバラになったモノたち -感想

感想
全体に業務コードで非常にありがちな内容なので非常に重要な章だと思った

5.1
 - staticメソッドや実質staticメソッドも作ってしまっているので気をつけたい
5.2
 - 初期化ロジックの分散もやりがち。目的別のファクトリメソッドを用意する手法も取り入れていきたい
5.3 共通処理クラス
 - 自分もよーく作りがち
 - 横断的関心事とそうでないものをしっかり意識して適切に設計したい
5.5 多すぎる引数
 - 業務コードでも非常にありがちだと思った
 - 自分もプリミティブ型執着は無意識のうちに陥っている気がするので気をつけたい
5.6 メソッドチェイン
 - 業務コードではstream使った実装で性質上非常によく見かける
  • 凝集度
    • ここでは、「クラス内における、データとロジックの関係性の強さを表す指標」
  • staticメソッドは低凝集になりがち

5.1.3 インスタンスメソッドのフリしたstaticメソッドに注意

  • インスタンス変数を使ってないメソッドもstaticメソッド同様

5.1.5 どういうときにstaticメソッドを使えばいいのか

  • 正しい使い方
    • 凝集度に影響がない場合
    • ログ出力用メソッド、フォーマット変換用メソッドなど
    • ファクトリメソッドとしてstaticメソッドを用いるのが良い

5.2.1 privateコンストラクタ+ファクトリメソッドで目的別初期化

  • 初期化ロジックの分散を防ぐためにファクトリメソッドを用意する
  • コンストラクタはprivateに、staticなファクトリメソッド
  • 目的別に用意

5.2.2 生成ロジックが増えすぎたらファクトリクラスを検討すること

  • 大きくなったら分離

5.3 共通処理クラス(Common・Util)

  • Common, Utilなど。staticと同じ低凝集構造を招きやすい。
  • グローバル変数が出現しやすくなるなど悪影響が多い。

5.3.1 さまざまなロジックが雑多に置かれがち

5.3.2 オブジェクト指向設計の基本に立ち返ろう

5.3.3 横断的関心事

  • 横断的関心事
    • さまざまなユースケースに広く横断する事柄
      • ログ出力
      • エラー検出
      • デバッグ
      • 例外処理
      • キャッシュ
      • 同期処理
      • 分散処理
    • 横断的関心事であれば共通処理としてまとめ上げても良いかもしれない
      • staticメソッドでも

5.4 結果を消すために引数を使わないこと

  • 出力引数
    • 結果を返すための引数
  • 低凝集を招き重複を生みやすい
  • 関数の見た目わかりづらく、可読性も低下

5.5 多すぎる引数

  • 低凝集に陥る良くない構造

5.5.1 プリミティブ型執着

  • プリミティブ型執着
    • プリミティブ型を濫用したコード
    • 初心者、長年プリミティブ型中心でコード書いてきたプログラマが陥りがち
    • オブジェクト指向設計を基本にまずは一つ一つ丁寧にクラス化

5.5.2 意味のある単位ごとにクラス化する

5.6 メソッドチェイン

  • メソッドチェイン
  • デメテルの法則
    • 利用するオブジェクトの内部を知るべきではない

5.6.1 尋ねるな、目白命じろ

第6章 条件分岐 – 迷宮化した分岐処理を解きほぐす技法 –

6.1.1

  • 早期return
    • ネストの解消
    • 条件ロジックと実行ロジックを分離できる

早期returnとガード節、何が違うのかわからなくなり調べてみた。
結構混同して書かれているものが多いが、「早期return」(early return)という手法があり、そこで使われる具体的な書き方のことを「ガード節」と考えておけば良さそうだ。

6.2.5

  • 単一責任選択の原則
    • 単一責任則かと思ったらどうやら別だった
  • Strategyパターン

6.3.1 ポリシーパターンで条件を集約する

  • Policyパターン

6.4

  • リスコフの置換原則
    • 「基本型を継承型に置き換えても問題なく動作しなければならない」

6.5

「分岐を書きそうになったら、まずinterface設計!」

6.6

  • フラグ引数

7章 コレクション – ネストを解消する構造化技法 –

7.2.1、7.2.2

  • 早期continue、早期break
    • 早期returnの応用

7.3 低凝集なコレクション処理

  • コレクションに関する処理はあちこちに実装されてしまいがち
  • 低凝集になりやすい

7.3.1 コレクション処理をカプセル化する

  • ファーストクラスコレクション (First Class Collection)
    • コレクション型インスタンス変数
    • コレクション型インスタンス変数を不正状態から防御し、正常に操作するメソッド
    • コレクションの増減を伴う場合は、新しいオブジェクトを生成して返す
    • 外部に渡す際は、変更できなくする(unmodifiableListメソッド)

8章 密結合 – 絡まって解きほぐせない構造 –

  • 結合度
  • 密結合、疎結合
  • 責務
    • 「責任と義務。義務を果たすべき責任」
  • ソフトウェア設計における責務
    • 「ある関心事について、正常に動作するよう制御する責任」

8.1.3

  • 責任
    • 「自分の分担として、それだけはしなければならない任務(負担)」
    • 責任と責務は、この本では「同類の考え方として明確に区別しないものとします」とのこと
  • 「責任は、誰がその責任を負うべきか適用範囲とセットになります」
  • ソフトウェアにおける責任
    • 「ある関心事について、不正な動作にならないよう、正常に動作するよう制御する責任」
  • 単一責任の原則
    • 「クラスが担う責任は、たったひとつに限定すべき」

8.1.5

  • 疎結合

8.1.6 DRY原則の誤用

  • 「同じようなロジックが複数あるからといって、責務を考えず無理にひとまとめにすると責務が多重になります」
  • DRY原則 (Don’t Repeat Yourself)
    • 『達人プログラマー』
      • 「すべての知識はシステム内において、単一、かつ明確な、そして信頼できる表現になっていなければならない」
    • 引用されたのは初版のものだったようなので、手元にあった、『達人プログラマー 第2版』を見返してみた。
      • p43 「コードは同じですが、これらコードが表現している知識は異なっているのです。これら2つの関数は、異なる2つのものごtが同じ規則を有しているということを示しているだけです。それは偶然であり二重化ではありません」
  • 「DRYにすべきは、それぞれの概念単位なのです」
  • 「同じようなロジック、似ているロジックであっても、概念が違えばDRYにすべきではないのです」
    • 自分はこれまで正しく理解できていなかった!
  • コードの重複を許さないのはOAOO原則( Once and Only Once)というらしい

8.2.1

  • 「継承はよっぽど注意して扱わないと危険、継承は推奨しませんというのが本書のスタンス」
  • スーパークラス依存
  • 継承より委譲
    • 委譲とは
      • コンポジション構造

8.2.2

8.2.5

  • 「高凝集を意図して強く関係していそうなロジックを一箇所にまとめ上げようとしたものの、結果として密結合に陥っているケースは非常に多く見られます」
  • 疎結合高凝集

8.2.6

  • スマートUI

8.2.7

  • 巨大データクラス

8.2.8

  • トランザクションスクリプトパターン
    • 「メソッド内に一連の処理手順がダラダラと長く書き連ねられている構造」
    • →超よく見る

8.2.9

  • 神クラス

9章 設計の健全性をそこなうさまざまな悪魔たち

9.1 デッドコード

  • デッドコード、到達不能コード

9.2 YAGNI原則

  • YAGNI
    • You aren’t going to need it.

9.3

  • マジックナンバー

9.6 null問題

  • nullを返さない
  • nullを渡さない
  • static finalなインスタンス変数を使う

9.6.2

  • null安全
  • null非許容型
    • Kotlin

9.7

  • 例外の握り潰し

9.9

  • 技術駆動パッケージング
  • ビジネス概念として強く関係しあうもの同士が一緒になるようにする

9.11

  • 「設計にbestはありません。常にbetterを目指しましょう。」

10章 名前設計 – あるべき構造を見破る名前 –

10.2.4

  • ラバーダッキング

10.4.3

  • 驚き最小の原則

10.5.1

  • DTO (Data Transfer Object)

10.6.1

  • 関心事が異なるメソッドは、「動詞+目的語」形式の名前になる傾向がある

10.6.2

  • 可能な限り動詞1語で済むよう名前設計するのがコツ
  • 「目的語」はクラス化

11章 コメント – 保守と変更の正確性を高める書き方 –

11.1

  • 退化コメント

11.1.1

  • 「言葉は話者や書き手の意思の劣化コピーにすぎません」

12章 メソッド(関数) – 良きクラスには良きメソッドあり –

12.3

  • カプセル化の話は、『Clean Code』p.145 データ転送オブジェクトでも同様に説明されている。

12.4

  • コマンド・クエリ分離 (CQS)

13章 モデリング – クラス設計の土台 –

13.2

  • 辞書のシステム定義
    • 「きわめて多数の構成要素から成る集合体で、各部分が有機的に連繋して、全体としてひとつの目的を持った仕事をするもの」
  • 「システムは目的達成のための手段」
  • 「システム構造を説明するために、単純な箱で図式化したものをモデル」
  • 「特定の目的達成のために最低限考慮が必要な要素を備えたものがモデル」

13.3.5

  • 「特定の目的に特化して設計することで、変更に強い高品質な構造になる」
  • 「責務よりも目的が先にくる」

14章 リファクタリング – 既存コードを成長に導く技 –

15章 設計の意義と設計への向き合い方

15.1 本書はなんの設計について書いたものなのか

  • 設計とは
    • 「課題を効率的に解決するしくみづくり」
  • ソフトウェアにおける設計とは
    • 「なんらかのソフトウェア品質特性の向上を促進するためのしくみをつくること」
  • 保守性
    • 「システムを修正する有効性や効率の度合い」
    • 「特に変更容易性の向上を目的にした設計手法なのです」

→最初にここを読んでも良いかもしれない

16章 設計を妨げる開発プロセスとの戦い

16.1.2

  • コンウェイの法則

16.1.3

  • 心理的安全性

16.2.3

  • 「仕様変更の際、最低でもメモ書き程度のクラス図を描きましょう」

16.2.4

  • 「厳密に設計しすぎるのはおすすすめしません」
  • 「たった一度の設計では、良き構造は見いだせません」

16.2.5

  • 早すぎる最適化
    • 「ボトルネックがわからないうちから高速に動作するコードをやみくもに書くこと」

16.3.2

  • ジョシュアツリーの法則
    • 「名前がないと存在を知覚できないとする認知法則」

16.3.3

16.4.3

  • GoogleのChormiumプロジェクトのレビュー指針
    • 「終わりを見つける」
    • 『「絶対に間違いないと保証する」ではなく、「よさそうです」でレビューを適切に終えます』
      • なるほど、気を付けよう・・・

17章 設計技術の理解の深め方

17.2.1

  • インプットは2、アウトプットは8
    • なるほど、最近はインプットが少なすぎると反省があったのでインプットが8くらいになっていたが、結局は定着のためにそれくらいは必要ということか。
  • 設計効果を必ず意識すること
    • 「一番良くないのは、設計効果を大して意識せず、構造だけをまねて満足してしまうこと」

リンク

「クソコード」動画

取り入れたいこと

  • 引数は不変にする
  • nullを渡さない・返さない
  • 値オブジェクトを積極的に作るようにしたい
タイトルとURLをコピーしました