良いコード/悪いコードで学ぶ設計入門―保守しやすい 成長し続けるコードの書き方
技術評論社
著者:仙塲大也
はじめに
謝辞
第1章 悪しき構造の弊害を知覚する
1.1 意味不明な命名
1.2 理解を困難にする条件分岐のネスト
1.3 さまざまな悪魔を招きやすいデータクラス
1.3.1 仕様変更時に牙をむく悪魔
1.3.2 重複コード
1.3.3 修正漏れ
1.3.4 可読性低下
1.3.5 未初期化状態(生焼けオブジェクト)
1.3.6 不正値の混入
1.4 悪魔退治の基本
第2章 設計の初歩
2.1 省略せずに意図が伝わる名前を設計する
2.2 変数を使い回さない、目的ごとの変数を用意する
2.3 ベタ書きせず、意味のあるまとまりでメソッド化
2.4 関係し合うデータとロジックをクラスにまとめる
第3章 クラス設計 ―すべてにつながる設計の基盤―
3.1 クラス単体で正常に動作するよう設計する
3.1.1 悪魔に負けない、頑強なクラスの構成要素
3.1.2 すべてのクラスに備わる自己防衛責務
3.2 成熟したクラスへ成長させる設計術
3.2.1 コンストラクタで確実に正常値を設定する
3.2.2 計算ロジックをデータ保持側に寄せる
3.2.3 不変で思わぬ動作を防ぐ
3.2.4 変更したい場合は新しいインスタンスを作成する
3.2.5 メソッド引数やローカル変数にもfinalを付け不変にする
3.2.6 「値の渡し間違い」を型で防止する
3.2.7 現実の営みにはないメソッドを追加しないこと
3.3 悪魔退治の効果を検証する
3.4 プログラム構造の問題解決に役立つ設計パターン
3.4.1 完全コンストラクタ
3.4.2 値オブジェクト
Column 種類の異なる言語と本書のノウハウ
第4章 不変の活用 ―安定動作を構築する―
4.1 再代入
4.1.1 不変にして再代入を防ぐ
4.1.2 引数も不変にする
4.2 可変がもたらす意図せぬ影響
4.2.1 ケース 1 可変インスタンスの使い回し
4.2.2 ケース 2 関数による可変インスタンスの操作
4.2.3 副作用のデメリット
4.2.4 関数の影響範囲を限定する
4.2.5 不変にして予期せぬ動作を防ぐ
4.3 不変と可変の取り扱い方針
4.3.1 デフォルトは不変に
4.3.2 どんなとき可変にしてよいか
4.3.3 正しく状態変更するメソッドを設計する
4.3.4 コード外とのやりとりは局所化する
第5章 低凝集 ―バラバラになったモノたち―
5.1 staticメソッドの誤用
5.1.1 staticメソッドはインスタンス変数を使えない
5.1.2 インスタンス変数を使う構造につくり変える
5.1.3 インスタンスメソッドのフリしたstaticメソッドに注意
5.1.4 どうしてstaticメソッドが使われてしまうのか
5.1.5 どういうときにstaticメソッドを使えばいいのか
5.2 初期化ロジックの分散
5.2.1 privateコンストラクタ+ファクトリメソッドで目的別初期化
5.2.2 生成ロジックが増えすぎたらファクトリクラスを検討すること
5.3 共通処理クラス(Common・Util)
5.3.1 さまざまなロジックが雑多に置かれがち
5.3.2 オブジェクト指向設計の基本に立ち返ろう
5.3.3 横断的関心事
5.4 結果を返すために引数を使わないこと
Column C#のoutキーワード
5.5 多すぎる引数
5.5.1 プリミティブ型執着
5.5.2 意味のある単位ごとにクラス化する
5.6 メソッドチェイン
5.6.1 尋ねるな、命じろ
第6章 条件分岐 ―迷宮化した分岐処理を解きほぐす技法―
6.1 条件分岐のネストによる可読性低下
6.1.1 早期returnでネスト解消
6.1.2 見通しを悪くするelse句も早期returnで解決
6.2 switch文の重複
6.2.1 即座にswitch文を書いてしまう
6.2.2 同じ条件式のswitch文が複数書かれていく
6.2.3 仕様変更時の修正漏れ(case文追加漏れ)
6.2.4 爆発的に増殖するswitch文の重複
6.2.5 条件分岐を一箇所にまとめる
6.2.6 よりスマートにswitch文重複を解消する interface
6.2.7 interfaceをswitch文重複に応用(ストラテジパターン)
Column クソコード動画「switch文」
6.3 条件分岐の重複とネスト
6.3.1 ポリシーパターンで条件を集約する
6.4 型チェックで分岐しないこと
6.5 interfaceの使いこなしが中級者への第一歩
6.6 フラグ引数
6.6.1 メソッドを分離する
6.6.2 切り替え機構をストラテジパターンで実現する
第7章 コレクション ―ネストを解消する構造化技法―
7.1 わざわざ自前でコレクション処理を実装してしまう
Column 車輪の再発明
7.2 ループ処理中の条件分岐ネスト
7.2.1 早期continueで条件分岐のネストを解消する
7.2.2 早期breakもネスト解消に役立つ
7.3 低凝集なコレクション処理
7.3.1 コレクション処理をカプセル化する
7.3.2 外部へ渡す場合はコレクションを変更できなくする
第8章 密結合 ―絡まって解きほぐせない構造― 141
8.1 密結合と責務
8.1.1 発生するさまざまなバグ
8.1.2 ロジックの置き場所がちぐはぐ
8.1.3 単一責任の原則
8.1.4 単一責任の原則違反で生まれる悪魔
8.1.5 責務が単一になるようクラスを設計する
8.1.6 DRY原則の誤用
Column クソコード動画「共通化の罠」
8.2 密結合の各種事例と対処方法 8.2.1 継承に絡む密結合
Column クソコード動画「継承」
8.2.2 インスタンス変数ごとにクラス分割可能なロジック
8.2.3 なんでもpublicで密結合
8.2.4 privateメソッドだらけ
8.2.5 高凝集の誤解から来る密結合
8.2.6 スマート UI
8.2.7 巨大データクラス
8.2.8 トランザクションスクリプトパターン
8.2.9 神クラス
8.2.10 密結合クラスの対処法
第9章 設計の健全性をそこなうさまざまな悪魔たち
9.1 デッドコード
9.2 YAGNI原則
9.3 マジックナンバー
9.4 文字列型執着
9.5 グローバル変数
9.5.1 影響範囲を最小化するよう設計すること
9.6 null問題
9.6.1 nullを返さない、渡さない
9.6.2 null安全
9.7 例外の握り潰し
9.7.1 原因分析困難に陥り開発者を疲弊させる
9.7.2 問題検出時にけたたましく叫ばせる
9.8 設計秩序を破壊するメタプログラミング
9.8.1 リフレクションによるクラス構造および値の変更
9.8.2 型の強みを活かせなくなるハードコード
9.8.3 デメリットを理解し用途を限定すること
9.9 技術駆動パッケージング
9.10 サンプルコードのコピペ
9.11 銀の弾丸
第10章 名前設計 ―あるべき構造を見破る名前―
10.1 悪魔を呼び寄せる名前
10.1.1 関心の分離
10.1.2 関心事にふさわしい命名
10.1.3 大雑把で意味が不明瞭な名前
10.2 名前を設計する―目的駆動名前設計
10.2.1 可能な限り具体的で、意味範囲が狭い、目的に特化した名前を選ぶ
10.2.2 存在ベースではなく、目的ベースで名前を考える
10.2.3 どんなビジネス目的があるか分析する
10.2.4 声に出して話してみる
10.2.5 利用規約を読んでみる
10.2.6 違う名前に置き換えられないか検討する
10.2.7 疎結合高凝集になっているか点検する
10.3 設計時の注意すべきリスク
10.3.1 名前無頓着になるな
10.3.2 仕様変更時の「意味範囲の変化」に警戒
10.3.3 会話には登場するのにコード上に登場しない名前に注意
10.3.4 形容詞で区別が必要なときはクラス化のチャンス
10.4 意図がわからない名前
10.4.1 技術駆動命名
Column 技術駆動命名を用いる分野もある
10.4.2 ロジック構造をなぞった名前
10.4.3 驚き最小の原則
10.5 構造を大きく歪ませてしまう名前
10.5.1 データクラスに陥る名前
10.5.2 クラスが巨大化する名前
Column クソコード動画「Managerクラス」
10.5.3 状況によって意味や扱いが異なる名前
10.5.4 連番命名
10.6 名前的に居場所が不自然なメソッド
10.6.1 「動詞 + 目的語」のメソッド名に注意
10.6.2 可能な限り動詞 1 語で済む名前にする
10.6.3 不適切な居場所のbooleanメソッド
10.7 名前の省略
10.7.1 意図がわからなくなる省略
10.7.2 基本的に名前は省略しないこと
10.7.3 そのほか省略をどう判断するか
第11章 コメント ―保守と変更の正確性を高める書き方―
11.1 退化コメント
11.1.1 コメントは劣化コピーにすぎないことを理解すること
11.1.2 ロジックの挙動をなぞるだけのコメントは退化しやすい
11.2 コメントで命名をごまかす
11.3 意図や仕様変更時の注意点を読み手に伝えること
11.4 コメントのルール まとめ
11.5 ドキュメントコメント
第12章 メソッド(関数) ―良きクラスには良きメソッドあり―
12.1 必ず自身のクラスのインスタンス変数を使うこと
12.2 不変をベースに予期せぬ動作を防ぐ関数にすること
12.3 尋ねるな、命じろ
Column クソコード動画「カプセル化」
12.4 コマンド・クエリ分離
12.5 引数
12.5.1 引数は不変にすること
12.5.2 フラグ引数は使わない
12.5.3 nullを渡さない
12.5.4 出力引数は使わない
12.5.5 引数は可能な限り少なく
12.6 戻り値
12.6.1 「型」を使って戻り値の意図を表明すること
12.6.2 nullを返さない
12.6.3 エラーは戻り値で返さない、例外をスローすること
Column メソッドの名前設計
Column staticメソッドの扱いに注意
第13章 モデリング ―クラス設計の土台―
13.1 邪悪な構造に陥りがちなUserクラス
13.2 モデリングの考え方とあるべき構造
13.2.1 システムとは何か
13.2.2 システム構造とモデリング
13.2.3 ソフトウェア設計におけるモデリング
13.3 良くないモデルの問題点と解決方法
13.3.1 Userとシステムの関係
13.3.2 仮想世界を表現する情報システム
13.3.3 目的別にモデリングする
13.3.4 モデルはモノではなく目的達成手段
13.3.5 単一責任とは単一目的
13.3.6 モデルの見直し方
13.3.7 モデルと実装は必ず相互にフィードバックする
Column クソコード動画「Userクラス」
13.4 機能性を左右するモデリング
13.4.1 裏に隠れた真の目的を見破る
13.4.2 機能性をイノベートする「深いモデル」
第14章 リファクタリング ―既存コードを成長に導く技―
14.1 リファクタリングの流れ
14.1.1 ネストを解消し、見通しを良くする
14.1.2 意味のある単位にロジックをまとめる
14.1.3 条件を読みやすくする
14.1.4 ベタ書きロジックを目的を表すメソッドに置き換える
14.2 ユニットテストでリファクタリングのミスを防ぐ
14.2.1 コードの課題を整理する
14.2.2 テストコードを用いたリファクタリングの流れ
14.3 あやふやな仕様を理解するための分析方法
14.3.1 仕様分析方法 1:仕様化テスト
14.3.2 仕様分析方法 2:試行リファクタリング
14.4 IDEのリファクタリング機能
14.4.1 リネーム(名前の変更)
14.4.2 メソッド抽出
14.5 リファクタリングで注意すべきこと
14.5.1 機能追加とリファクタリングを同時にやらない
14.5.2 スモールステップで実施する
14.5.3 無駄な仕様は削除することも視野に
Column Railsアプリのリファクタリング
第15章 設計の意義と設計への向き合い方
15.1 本書はなんの設計について書いたものなのか
15.2 設計しないと開発生産性が低下する
15.2.1 要因 1:バグを埋め込みやすくなる
15.2.2 要因 2:可読性が低下する
15.2.3 木こりのジレンマ
15.2.4 一生懸命仕事した感覚だけが残って生産性は悪いまま
15.2.5 国家規模の経済損失
15.3 ソフトウェアとエンジニアの成長性
15.3.1 エンジニアにとっての資産とは何か
15.3.2 レガシーコードに人は引きずられやすい
15.3.3 レガシーコードは高品質設計を妨げる
15.3.4 レガシーコードは開発工数を減少させる
15.4 課題を解決する 15.4.1 課題が見えないとそもそも設計する意識が生まれない
15.4.2 知覚容易な課題と知覚困難な課題がある
15.4.3 理想形を知ってはじめて課題を知覚できる
15.4.4 変更容易性を比較できないジレンマ
15.5 コードの良し悪しを判断する指標
15.5.1 実行可能コードの行数Columnクラスを分割すると読みにくくなる?
15.5.2 循環的複雑度
15.5.3 凝集度
15.5.4 結合度
15.5.5 チャンク
15.6 コード分析をサポートする各種ツール
15.6.1 Code ClimateQuality
15.6.2 Understand
15.6.3 Visual Studio
Column シンタックスハイライトを品質可視化に利用する
15.7 設計対象と費用対効果
15.7.1 パレートの法則(80:20 の法則)
15.7.2 サービスの中心的領域、コアドメイン
15.7.3 重点設計対象の選定にはビジネス知識が必要
15.8 時間を操る超能力者になろう
第16章 設計を妨げる開発プロセスとの戦い
16.1 コミュニケーション
16.1.1 コミュニケーションが希薄だと設計品質に問題が生じる
16.1.2 コンウェイの法則
16.1.3 心理的安全性
16.2 設計
16.2.1 「早く終わらせたい」心理が品質低下の罠
16.2.2 粗悪なコードはきれいなコードを書くより常に遅い
16.2.3 クラス設計と実装のフィードバックサイクルを回す
16.2.4 厳密に設計しすぎず、サイクルを回し続けるのがコツ
16.2.5 「パフォーマンスが落ちるからクラスを追加しない」は正しい?
16.2.6 設計ルールを多数決で決めるとコード品質は最低になる
16.2.7 設計ルールづくりのポイント
16.3 実装
16.3.1 割れ窓理論とボーイスカウトの規則
16.3.2 既存コードを信用せず、冷静に正体を見破る
16.3.3 コーディング規約を利用しよう
16.3.4 命名規約
16.4 レビュー
16.4.1 コードレビューをしくみ化しよう
16.4.2 コードを設計視点でレビューしよう
16.4.3 敬意と礼儀
16.4.4 定期的に改善タスクを棚卸しすること
16.5 チームの設計力を高める
16.5.1 影響力を持つレベルにまで仲間を集める
16.5.2 基本はスモールステップ
16.5.3 実感が大事、手を動かしてみよう
16.5.4 フォローアップ勉強会を開いてみよう
16.5.5 勉強会のバッドノウハウ
16.5.6 リーダーやマネージャーに設計と費用対効果の話をする
16.5.7 設計責任者を立てる
第17章 設計技術の理解の深め方
17.1 さらにステップアップするための設計技術書紹介
17.1.1 現場で役立つシステム設計の原則〜変更を楽で安全にするオブジェクト指向の実践技法
17.1.2 リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック
17.1.3 リファクタリング 既存のコードを安全に改善する(第2版)
17.1.4 Clean Codeアジャイルソフトウェア達人の技
17.1.5 レガシーコード改善ガイド
17.1.6 レガシーソフトウェア改善ガイド
17.1.7 レガシーコードからの脱却 ―ソフトウェアの寿命を延ばし価値を高める9つのプラクティス
17.1.8 エンジニアリング組織論への招待〜不確実性に向き合う思考と組織のリファクタリング
17.1.9 プリンシプル オブ プログラミング3年目までに身につけたい一生役立つ101の原理原則
17.1.10 Clean Architecture達人に学ぶソフトウェアの構造と設計
17.1.11 エリック・エヴァンスのドメイン駆動設計
17.1.12 セキュア・バイ・デザイン 安全なソフトウェア設計
17.1.13 ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本
17.1.14 ドメイン駆動設計 モデリング/実装ガイド
17.1.15 ドメイン駆動設計 サンプルコード&FAQ
17.1.16 テスト駆動開発
Column バグ退治 RPG『バグハンター2 REBOOT』
17.2 設計スキルを高める学び方
17.2.1 学習のための指針
17.2.2 悪魔の構造を見破る練習
17.2.3 リファクタリングで大幅スキルアップ
Column C#と長き旅、そして設計への道
17.2.4 動くコードを書いたら、設計し直してからコミット
17.2.5 設計技術書でさらなる高みを目指そう
参考文献
索引