良いコード/悪いコードで学ぶ設計入門 ――保守しやすい 成長し続けるコードの書き方(仙塲大也)
書籍情報
- 著者:仙塲大也(著)
- 発行日:2022-04-30
- ISBN:9784297127831
- URL:https://gihyo.jp/book/2022/978-4-297-12783-1
書籍目次
- はじめに
- 謝辞
- 第1章 悪しき構造の弊害を知覚する
- 1.1 意味不明な命名
- 1.2 理解を困難にする条件分岐のネスト
- 1.3 さまざまな悪魔を招きやすいデータクラス
- 1.4 悪魔退治の基本
- 第2章 設計の初歩
- 2.1 省略せずに意図が伝わる名前を設計する
- 2.2 変数を使い回さない,目的ごとの変数を用意する
- 2.3 ベタ書きせず,意味のあるまとまりでメソッド化
- 2.4 関係し合うデータとロジックをクラスにまとめる
- 第3章 クラス設計 ―すべてにつながる設計の基盤―
- 3.1 クラス単体で正常に動作するよう設計する
- 3.2 成熟したクラスへ成長させる設計術
- 3.3 悪魔退治の効果を検証する
- 3.4 プログラム構造の問題解決に役立つ設計パターン
- Column 種類の異なる言語と本書のノウハウ
- 第4章 不変の活用 ―安定動作を構築する―
- 4.1 再代入
- 4.2 可変がもたらす意図せぬ影響
- 4.3 不変と可変の取り扱い方針
- 第5章 低凝集 ―バラバラになったモノたち―
- 5.1 staticメソッドの誤用
- 5.2 初期化ロジックの分散
- 5.3 共通処理クラス(Common・Util)
- 5.4 結果を返すために引数を使わないこと
- Column C#のoutキーワード
- 5.5 多すぎる引数
- 5.6 メソッドチェイン
- 第6章 条件分岐 ―迷宮化した分岐処理を解きほぐす技法―
- 6.1 条件分岐のネストによる可読性低下
- 6.2 switch文の重複
- Column クソコード動画「switch文」
- 6.3 条件分岐の重複とネスト
- 6.4 型チェックで分岐しないこと
- 6.5 interfaceの使いこなしが中級者への第一歩
- 6.6 フラグ引数
- 第7章 コレクション ―ネストを解消する構造化技法―
- 7.1 わざわざ自前でコレクション処理を実装してしまう
- Column 車輪の再発明
- 7.2 ループ処理中の条件分岐ネスト
- 7.3 低凝集なコレクション処理
- 第8章 密結合 ―絡まって解きほぐせない構造―
- 8.1 密結合と責務
- Column クソコード動画「共通化の罠」
- 8.2 密結合の各種事例と対処方法
- Column クソコード動画「継承」
- 第9章 設計の健全性をそこなうさまざまな悪魔たち
- 9.1 デッドコード
- 9.2 YAGNI原則
- 9.3 マジックナンバー
- 9.4 文字列型執着
- 9.5 グローバル変数
- 9.6 null問題
- 9.7 例外の握り潰し
- 9.8 設計秩序を破壊するメタプログラミング
- 9.9 技術駆動パッケージング
- 9.10 サンプルコードのコピペ
- 9.11 銀の弾丸
- 第10章 名前設計 ―あるべき構造を見破る名前―
- 10.1 悪魔を呼び寄せる名前
- 10.2 名前を設計する―目的駆動名前設計
- 10.3 設計時の注意すべきリスク
- 10.4 意図がわからない名前
- Column 技術駆動命名を用いる分野もある
- 10.5 構造を大きく歪ませてしまう名前
- Column クソコード動画「Managerクラス」
- 10.6 名前的に居場所が不自然なメソッド
- 10.7 名前の省略
- 第11章 コメント ―保守と変更の正確性を高める書き方―
- 11.1 退化コメント
- 11.2 コメントで命名をごまかす
- 11.3 意図や仕様変更時の注意点を読み手に伝えること
- 11.4 コメントのルール まとめ
- 11.5 ドキュメントコメント
- 第12章 メソッド(関数) ―良きクラスには良きメソッドあり―
- 12.1 必ず自身のクラスのインスタンス変数を使うこと
- 12.2 不変をベースに予期せぬ動作を防ぐ関数にすること
- 12.3 尋ねるな,命じろ
- Column クソコード動画「カプセル化」
- 12.4 コマンド・クエリ分離
- 12.5 引数
- 12.6 戻り値
- Column メソッドの名前設計
- Column staticメソッドの扱いに注意
- 第13章 モデリング ―クラス設計の土台―
- 13.1 邪悪な構造に陥りがちなUserクラス
- 13.2 モデリングの考え方とあるべき構造
- 13.3 良くないモデルの問題点と解決方法
- Column クソコード動画「Userクラス」
- 13.4 機能性を左右するモデリング
- 第14章 リファクタリング ―既存コードを成長に導く技―
- 14.1 リファクタリングの流れ
- 14.2 ユニットテストでリファクタリングのミスを防ぐ
- 14.3 あやふやな仕様を理解するための分析方法
- 14.4 IDEのリファクタリング機能
- 14.5 リファクタリングで注意すべきこと
- Column Railsアプリのリファクタリング
- 第15章 設計の意義と設計への向き合い方
- 15.1 本書はなんの設計について書いたものなのか
- 15.2 設計しないと開発生産性が低下する
- 15.3 ソフトウェアとエンジニアの成長性
- 15.4 課題を解決する
- 15.5 コードの良し悪しを判断する指標
- Column クラスを分割すると読みにくくなる?
- 15.6 コード分析をサポートする各種ツール
- Column シンタックスハイライトを品質可視化に利用する
- 15.7 設計対象と費用対効果
- 15.8 時間を操る超能力者になろう
- 第16章 設計を妨げる開発プロセスとの戦い
- 16.1 コミュニケーション
- 16.2 設計
- 16.3 実装
- 16.4 レビュー
- 16.5 チームの設計力を高める
- 第17章 設計技術の理解の深め方
- 17.1 さらにステップアップするための設計技術書紹介
- Column バグ退治RPG『バグハンター2 REBOOT』
- 17.2 設計スキルを高める学び方
- Column C#と長き旅,そして設計への道
第1章 悪しき構造の弊害を知覚する
- 意味不明な命名の話
- 条件分岐のネストが深いとツラい問題
- データクラスの弊害
第2章 設計の初歩
- 変数や関数の名前を略すな
- 変数を使い回すな
- ロジックをダラダラとベタ書きするな、意味のあるまとまりでメソッド化しろ
- 関係するデータとロジックはクラスにまとめろ
第6章 条件分岐 ―迷宮化した分岐処理を解きほぐす技法―
条件分岐のネストによる可読性低下
- 複数の条件を満たすことで実行されるコード(条件分岐がネストしたコード)は見通しが悪い
- 仕様変更も困難
早期returnでネスト解消
- T/O
else句も早期return
- 関数の末尾まで戻り値を引き回さず、else句の条件に一致した時点で早期return
- Code Completeにも書いてあるテクニック
switch文の重複
- コード各所に安易にswitch文を書くと重複したswitch文が増殖する
- 仕様変更時の修正が大変&修正漏れのリスクも大きい
条件分岐はひとつにまとめる
- 単一責任原則
interfaceでswitch文を解消する
- (1) switch文で行われる処理を表すinterfaceを定義
- (2) interfaceを実装したクラスを作成
- (3) switch文の条件分岐内で行われる処理をクラスに移動(条件の数だけクラスを作る)
- (4) 条件分岐の条件と各クラスのインスタンスが入ったHashMapを作る
- [MEMO]
- Factoryでも良さそうだが、HashMapにすることでコード上から条件分岐を完全に無くせるというメリットもある
- とはいえ、どのみち全てのパターンが正常に動作するかのテストはするのだからif文で実装しても変わらないのでは? という感もある
ポリシーパターンで条件を集約する
- 書籍内のコード参照
型チェックで分岐しない
- if文で型チェックしていると結局条件分岐を書くことになるので意味がない
- Factoryパターンで隠蔽するか上記のMapに格納するテクニックを使う
フラグ引数は避ける
- フラグ引数とは関数/メソッドの挙動を引数として与えたフラグで制御するテクニック(アンチパターン)
- フラグ引数があると、メソッド内部を参照しないと挙動が把握できなくなるので使うべきではない
- 単一責務原則にも反する
メソッドは単機能にする
- フラグ引数で制御せず、別のメソッドに分割する
- 呼び出すメソッドは変えたくないが、ロジックは切り替えたい場合はストラテジーパターンを採用する
第7章 コレクション ―ネストを解消する構造化技法―
コレクション処理は標準ライブラリを探してみる
- コレクション処理は標準ライブラリに便利なメソッドが用意されている事が多い
- リストを走査するような処理は自前で処理を書く前に利用できそうなライブラリがないか調べてみる
ループ処理中の条件分岐
- 条件分岐をネストさせずに早期continue/breakさせろ
コレクションに対する操作はファーストクラスコレクションに集約しろ
- ファーストクラスコレクションでコレクションに対する操作をカプセル化する
- ファーストクラスコレクション → コレクションをインスタンス変数に持つクラス
- コレクション自体を渡す際は、コレクションを変更不能にする(言語機能orそのようなクラスを作る)
第10章 名前設計 ―あるべき構造を見破る名前―
目的駆動名前設計
- 本書の基本的な考え方
- ソフトウェアで達成したい目的をベースにした名前設計
- 名前から目的や意図が読み取れるよう命名を工夫する
悪例: 商品クラス
- ECサイトの商品を表現するクラスに「商品クラス」と名前を付けるとする
- ECサイトは商品を中心に様々な処理が実行されるので、商品クラスは様々なユースケース(とそれが必要とするデータ)に強く密結合していき、クラスは巨大化/複雑化してしまう
- 結果的にひとつの変更が他の機能に影響を与えてしまうダメな設計に成り果ててしまう
関心の分離でクラスを分解する
- ユースケースや目的、役割ごとにクラスを分割する
- 商品クラスの例で言えば、以下のように分解できる
- ショッピングカート機能で扱う
- 予約機能で扱う商品
- 配達機能で扱う商品
- それぞれの役割に特化したクラスを作ることで保守性を保つことができる
目的不明オブジェクト
- 大雑把な概念を表すクラスは様々な概念/機能を集約しがちで、結果的にクラスが巨大化しがち
- 巨大化したクラスは保守性が低下する
- そうならないように関心の分離を意識して名前(とクラス)を設計することが必要
目的駆動名前設計のポイント
- 可能な限り具体的で意味範囲が狭い特化した名前を選ぶ
- 存在ベースではなく目的ベースで名前を考える
- どんな関心事があるか分析する
- 声に出して話してみる
- 違う名前に置き換えられないか検討する
- 疎結合高凝集になっているか点検する
名前設計での注意事項
- 名前に無頓着になりがち
- 仕様変更時の「意味範囲の変化」に警戒が必要
- 会話には登場するのにコード上に登場しない名前は要注意
- 形容詞で区別が必要な時はクラス化のチャンス
名前付けで発生しがちなアンチパターン
- 意図や目的がわからない名前
-
tmp
とかvalue
みたいな名前
-
- 技術駆動命名
- 実装に寄った名前
- ロジック構造をなぞった名前
- 驚き最小化原則
構造を大きく歪ませる名前
- データクラスに陥る名前
- クラスが巨大化する名前
- 状況によって意味や扱いが変わる名前
- 連番命名
第11章 コメント ―保守と変更の正確性を高める書き方―
退化コメント
- 実装が変化しても修正されておらず、内容が古くなったコメント
- コメントが偽情報になるので混乱を産む
- ロジックの挙動をなぞるだけのコメントは退化しやすい
コメントで命名を誤魔化す
- 意図が伝わりにくいメソッド名をコメントでフォローしている
- これもコメントが退化しやすい
- メソッド名をより実体を表した名前に変更するべき