一旦まとめ
7章を目前に、どの内容をどこで学んだんだっけ・・・?というのを自分なりにまとめておきたくなりました。
コード書いたからか、結構覚えているなーという感じです。
あと、本を先に進めたい!という気が勝っておざなりにしてるところがあるなぁ、と。
普遍的な変数のところですね。
Perl の場合には Readonly
か constant
を使うのですが・・・ううん(顔を背ける)
さて、7章以降もがんばってくぞー
第1章 悪しき構造の弊害を知覚する
弊害とは
- コードを読み解くのに時間がかかる
- バグを埋め込みやすくなる
- 悪しき構造がさらに悪しき構造を誘発する
意味不明な命名
何重にもネストしたロジック
- 巨大なネスト
- データを保持するだけのクラス
- データを扱うメソッドがない
- 重複コード(重複メソッド)がコードのあちらこちらに書かれてしまう
- 修正漏れが発生する可能性
- 可読性の低下
- 生焼けオブジェクト
- 未初期化状態のオブジェクトが利用できてしまう
- 当然利用しようとすれば undef になる
- 未初期化状態のオブジェクトが利用できてしまう
- 不正値の混入
- 悪魔退治の基本
- 悪しき構造の弊害を知る
- オブジェクト指向のクラスを適切に設計する
第2章 設計の初歩
- 省略せずに伝わる名前を設計する
- 変数を使いまわさない、目的ごとの変数を用意する
- 再代入を避ける
- 意味あるまとまりでメソッド化する
- サブルーチン化か
- 種類の異なる処理をメソッドにまとめる
- だらだらと書かない
- 理解のしやすさを優先に、行数や変数名の文字数が増えることを厭わない
- コードゴルフではない
- 関係し合うデータとロジックをクラスにまとめる
- データクラスの問題でも書かれていた「いろいろなところに類似のロジックが書かれる」問題
第3章 クラス設計
- オブジェクト指向は、ソフトウェアの品質向上を目的とする考え方の一種
- 適切なクラス設計により、保守や変更が容易になる
- 頑強なクラスの構成要素
- 自己防衛責務を持たせる
- 一つ一つのクラスが完結している
- NG:他のクラスによる初期化が必要
- NG:データ入力を他のクラスにしてもらう
- 一つ一つのクラスが完結している
- 成熟したクラスへ成長させる設計術
- 計算ロジックをデータ保持側に寄せる
- 不変で思わぬ動作を防ぐ
- 変数の上書きができると思わぬ副作用を招く
- final, const Readonly 使おう
- インスタンス変数を変更したい場合は、インスタンスごと作り直す
- メソッド引数やローカル変数も不変にする
- 「値の渡し間違い」を型で防止する
- 引き数をプリミティブ型にしない
- Int 型ではなく、Money 型などで渡す
- 同じ型同士で処理するように、メソッドの引数の型チェックを行う
- 引き数をプリミティブ型にしない
- 現実の営みにないメソッドを追加しない
プログラム構造の問題解決に役立つ設計パターン
- 完全コンストラクタ
- 生焼けオブジェクトを作らせない
- コンストラクタで生成する時点でしっかり引数入れて引き数チェックもする
- 生焼けオブジェクトを作らせない
- 値オブジェクト
- 値の概念そのものをクラスとして定義する
- 「値オブジェクト」+「完全コンストラクタ」はオブジェクト指向設計の最も基本形を体現している構造の一つ
第4章 不変の活用
- 再代入を避ける
- 不変にする
- 引数も不変にする
- 関数による可変インスタンスの操作
- 主作用(関数が値を受け取り、値を返す)以外の副作用が出ないようにする
- データを引数で受け取る
- 状態は変更しない
- 値は関数の戻り値として返す
- インスタンス変数を不変にしておくことで、副作用の余地をなくす
- 変えようとするとエラーになるから
- 意図して変えたい時はちゃんとメソッド作る
- 不変と可変の取り扱い方針
- デフォルトは不変
- スコープが局所的なケースのみ可変
- ループカウンタなど
- 可変の変数で状態を変更する時は、状態変更のみ発生するように設計する
- 副作用がないようにする
- コード外とのやり取りは局所化する
第5章 低凝集
- static メソッドを誤用しない
- 初期化ロジックの分散
- 共通処理クラス
- common, util などと名付けられるクラス
- 低凝集になりやすい
- static メソッドが入り込みやすい
- 第2章でいうところの「意味のあるまとまり」でまとめられるべきメソッドが、共通処理クラスに入れられる
- クラス設計の基本に立ち返る
- 横断的関心ごとを共通処理クラスにする
- static クラスにしても良い
- ログ出力
- エラー検出
- デバッグ
- 例外処理
- キャッシュ
- 同期処理
- 分散処理
- 結果を返すために引数を使わない(?)
- 多すぎる引数
- メソッドチェイン
- 似たようなコードが量産される原因の一つ
- デメテルの法則「利用するオブジェクトの内部を知るべきではない」
- 「尋ねるな、命じろ」
- 他のオブジェクトの状態を尋ねない。他のオブジェクトの状態に応じて呼び出し側が判断をしない
- 命じられた側で判断する
- 詳細なロジックは、呼ぶ側ではなく、呼ばれる側に実装する
第6章 条件分岐
- 条件分岐のネストによる可読性低下
- 早期 return で解消
- 条件と実行の分離
- 条件の追加が容易になる
- else 句をなくすことも可能
- 早期 return で解消
- switch 文の重複
- 条件分岐は同じだが、返り値だけ異なる switch 文が量産されやすい
- 量産されることで、仕様変更・条件追加・条件削除時の修正漏れが生じやすい
- switch 文は増えやすい
- 条件分岐は1箇所にまとめる
- interface を使い、スマートに重複を解決する
- 同名のメソッド(例:area)を引数で渡すクラスに実装しておく
- 呼ぶメソッドは area() で共通
- ダックタイピング
- Perl だと Mo[o|u]se::role
- interface は利用するクラスに共通のメソッドがあることを要求するので、実装漏れにも対処できる
- 条件分岐は同じだが、返り値だけ異なる switch 文が量産されやすい
- 条件分岐の重複とネスト
- ポリシーパターンで対処
- 条件の部品化、部品化した条件の組み替え
- 条件ごとのクラスを作、(例:GoldCustomer, SilverCustome)条件判定するメソッドを実装する(例:ok)
- okメソッドを持っているクラスを集約する interface を作る
- GoldCuster クラスでは、すべての条件を満たす、SilverCustomer クラスでは2つの条件を満たす、などの条件で実装する
- switch や if 文を使わずとも、条件分岐が可能に
- ポリシーパターンで対処
- 型チェックで分岐しない
- interface を実装しても、実装したメソッド側で型による分岐をしたのではもったいない
- 条件分岐削減の役に立っていない
- if や switch の代わりに interface が使えないかを考える
- interface を実装しても、実装したメソッド側で型による分岐をしたのではもったいない
- フラグ引数
- メソッド側で処理を分岐するためにつける引数
- 型オブジェクトを渡し、interface を実装して共通のメソッドで処理させる