rhanda | 元銀行員Web系エンジニアの日記

実務未経験からWeb系受託開発企業に転職したひよっこエンジニアが覚えたことや日々の感情を残すブログ

『良いコード悪いコードで学ぶ設計入門』読みました

構成

第1章 悪しき構造の弊害を知覚する
第2章 設計の初歩
第3章 クラス設計〜すべてにつながる設計の基盤〜
第4章 不変の活用 〜安定動作を構築する〜
第5章 低凝集〜バラバラになったモノたち〜
第6章 条件分岐〜迷宮化した分岐処理を解きほぐす技法〜
第7章 コレクション〜ネストを解消する構造化技法〜
第8章 蜜結合〜絡まって解きほぐせない構造〜
第9章 設計の健全性を損なうさまざまな悪魔たち
第10章 名前設計〜あるべき構造を見破る名前〜
第11章 コメント〜保守と変更の正確性を高める書き方〜
第12章 メソッド(関数)〜〜良きクラスには良きメソッドあり〜
第13章 モデリング〜クラス設計の土台〜
第14章 リファクタリング〜既存コードを成長に導く技〜
第15章 設計の意義と設計への向き合い方
第16章 設計を妨げる開発プロセスとの戦い
第17章 設計技術の理解の深め方

感想

具体的なコードを伴ったアンチパターンを紹介してくれるので、「こういう事態を回避するためにこうする」という論拠となる考え方をたくさん知ることができました。特にどういうふうに開発の効率が悪くなるのか、どのように開発者の生産性を下げるのか、嬉しくないのかが言語化されていて、日々自分が感じる「なんか好ましくない」がこういうことだったのかと理解できる感じがする本でした。


「目的ベースの命名設計」に関する話の中で、例えばユーザーも目的で分けると出品者購入者クラスに分けることができる(クラスを分けることで無関係なロジックを排除し、影響範囲を小さく変更しやすくできる)という話が載っていたのですが、その粒度でクラスを分けるのは結構難しそうだなあと感じました。
「ビジネスルールとクラスが一致していると正確に素早く変更しやすい」というのは確かにそうなのかもなと思いました。

メモ

  • デメテルの法則
    • 利用するオブジェクトの内部を知るべきではない。(💭よく見るやつ)
    • 他のオブジェクトの内部状態を尋ねたり、状態に応じて呼び出し側が判断するのではなく、呼び出し側はただ命ずるだけで、命令された側が判断・制御するようにする
  • 深いネストによる可読性低下
    • 極めて慎重に読み解く必要がある。開発者の思考に高い負荷がかかり疲弊させる
    • コードの見通しが悪くなる。
  • 早期リターン
    • メソッドの冒頭とそれ以降とで、条件ロジックと実行ロジックを分離できる
  • ソフトウェア設計における責務
    • ある関心ごとについて、正常に動作するよう制御する責任
  • 単一責任の原則
  • DRYの誤用
    • 同じようなロジックや似ているロジックであっても、概念が違えばDRYにすべきではない
    • 処理を共通化して良いかどうか判断するには、同じビジネス概念かを見分ける必要がある
    • ビジネス理解が進んだら「あるビジネス概念が実は複数の異なるビジネス概念だった」というのはありがち。それがわかった段階で共通化を解除するなど構造を見直すのがベター
    • 例えば、
      • 「通常割引メソッド」と「夏季セール割引メソッド」が割り引く値以外差異がなくロジックはほぼ同じな時に「重複コードではないか?」と思うかもしれない
      • しかし夏季セール割引の仕様が変わったとしたら、夏季セール割引のロジックは通常割引とは違うものになる
      • このように同じようなロジックが複数があるからと言って責務を考えず無理にひとまとめにすると責務が多重になる。
  • マジックナンバー
    • ロジック内に直接書き込まれている意図不明な数値。説明なき数値は開発者を混乱させる
  • 影響範囲を最小化するよう設計する
    • グローバル変数(および巨大データクラス)は影響範囲が広すぎる
    • 呼び出し箇所が少なく、局所化されているほどロジックの理解が容易になる
    • 目的駆動名前設計
      • プログラミングにおける名前の役割は可読性の向上だけではない
      • ソフトウェアで達成したい目的をベースに名前を設計(命名)する
      • 名前設計が不十分 → 様々なユースケースのクラスと関係を持つ → ロジック増える → クラス巨大化が起こりがち
      • クラスが巨大化 → 仕様変更が発生した場合に影響範囲が大きい → 開発生産性DOWN
      • 大雑把で意味がガバガバな名前は、あらゆるロジックを惹きつけてしまう心理的引力が働く
  • ラバーダッキング
    • 誰かに説明すると自ら原因に気づいて解決するという手法。声に出して話すのは分析行為として理にかなっている
    • 書籍『ドメイン駆動設計』のユビキタス言語(チーム全体で意図を共有するための言葉)が由来。
    • 同じ意図の名前を会話・ドキュメント・クラス名やメソッド名で共通して使うことで、意図の減衰を防止し設計のいびつさ解消に役立つ
  • 意図がわからない名前
    • 理解の難しさから翻訳作業が都度必要になる(💭超わかる)
    • 例えば仕様変更の依頼があったとき、対応するメソッドや変数がなんなのか頭の中で翻訳作業が必要になる
    • 新しくジョインしたメンバーへの説明コストUP
    • スプレッドシートで用語集などが作られるケースがあるが、この手の資料はメンテナンスがおろそかになりがち
    • 人間の注意力には限界があるので、仕様とロジックを相互にいつでも正確に翻訳できるわけではない。不注意により誤って解釈する可能性がある。
    • また意図不明な名前は解釈の誤りをさらに増大させる(💭ついこの間起こった気がする)
    • 技術駆動命名
      • 技術ベースでの命名のこと。型名を表すintなど用語に基づいた名前が用いられたりして、意図がわかりにくくなりがち
      • 実現したい目的・意図がわかるように命名しましょう
    • 連番命名
      • クラスやメソッドに対して番号付で命名すること
      • 目的・意図が読み取れない点で技術駆動命名と似ているが、構造改革が困難な点において連番命名は悪質
      • 番号付で管理されているために連番命名以外で命名すると番号の秩序が失われることから、番号で管理したい側からの反発を招きかねず命名を見直しづらい
    • 基本的に名前は省略しない
      • 可読性が上がり他のメンバーや将来の自分を助ける(💭わかる)
      • 昔はタイプ文字数が多いときのタイポしやすさなどから、長い命名は嫌われたりしていた。最近はエディタが補完したりするのでその労力は気にならない
      • SNSやVIPのような、一般的にも省略されているものは例外と筆者は考える(💭わかる)
    • 再説明コメントで命名をごまかす
      • 意味が伝わりにくいメソッドでは再説明コメントが書かれがち(💭わかる)
      • あまり可読性に貢献しない上、メソッドが変わるたびにコメントの更新作業も発生
      • ロジックをなぞるだけのコメントは退化しがち
        • (実装が変わったのにメンテナンスされず実装を正しく説明しなくなること)
      • ダメなメソッド名をコメントで補足説明するのではなく、メソッド名自体をブラッシュアップしましょう
  • 情報システムとは
    • 現実世界の概念のみをコンピュータの世界へ投影した仮想現実といえる
    • 現実世界の概念をコンピュータの仮想世界へ変換し、意味を対応づけ、そして概念的なやり取りをコンピュータによって高速化することで高速化していると考えられる
    • 現実世界での物理的な存在と情報システム上のモデルが1:1になるとは限らず、1:多になるケースが多いのが特徴
      • GitHubのユーザー設定では、目的ごとにアカウント・プロフィールなどと目的ごとにモデルが分かれて表現されている
  • 設計しないと開発生産性が低下する
    • レガシーコード
      • 変更が困難で壊れやすいコード。またレガシーコードが蓄積している状態を技術的負債と呼ぶ
    • 開発生産性が低下する要因は主に次の2つ
      1. バグを埋め込みやすくなる
        • 正確に変更できるまで時間がかかる
      2. 可読性低下
        • ロジックの見通しが悪い、意図が読み取りづらいetc.
    • レガシーコードは高品質設計を妨げる
      • レガシーコードは極めてアンバランスでトリッキーがちなため、設計改善が困難になり、納期の都合などから諦めてしまうことが多い
      • → 高品質な設計実装の経験を積めなくなり、設計スキル向上を果たせなくなる
    • レガシーコードは開発工数を減少させる
      • レガシーコードは理解に多大な時間を要する(💭わかる)。一方で時間は有限
      • 本来もっと価値の高い仕事に充てられる時間が目減りしてしまう