オブジェクト指向設計実践ガイド ~Rubyでわかる進化し続ける柔軟アプリケーションの育て方の7~9章を読んで
上記の本の7~9章を読んで印象に残った部分をまとめました。
7章
- 継承の手法を使い「ロール(役割)」を共有する解決法について
- クラスの継承などの関連がなかったオブジェクト同士が持つ共通の振る舞い「ロール」
- 隠れたロール(役割)を明らかにし、その振る舞いをすべての担い手どうしで共有するためのコードを書いていく
- Preparerロールの存在が示唆するのは、対応するPreparableロールの存在(対となるロールの存在)
- 共有の振る舞いをモジュールに入れる正しい方法
- クラス名をいくつも確認したうえで、ある変数に代入する値を決定するくらいなら、変数名をメソッドにして、オブジェクトに問い合わせるようにする
- メッセージに基づく期待はクラスの境界を超越し、ロールを明らかにする
- オブジェクトは自身の振る舞いは自身で持つべき
- 一つの具象クラスでまずロールの振る舞いを実装し、それを複数の具象クラスで使えるものに再構成する
- クラスによる継承はis-aの関係、モジュールによる継承はbehaves-like-aの関係
- ロールの設計のアンチパターン
- typeやcategoryによってどんなメッセージを送るかselfに送るか決めている => クラスによる継承を使った方が良い
- メッセージを受け取るオブジェクトのクラスを確認してから、どのメッセージを送るかをオブジェクトが決めている => ダックタイプを見落としてしまっている
- 一部のクラスでしか使わないようなコードがモジュールに置かれている
- サブクラスはスーパークラスのインターフェースに含まれるどのメッセージが来ても応えられるべきであり、同じ種類の入力を取り、同じ種類の出力を行わなければならない
- ほかのオブジェクトに自身の型を識別させ、自身の扱いや何が期待できるのかを決めさせることは、どんなことであっても許されない
- 継承可能なコードを書くための最も基本的なコーディングの手法は、テンプレートメソッドパターンを使うこと
- テンプレートメソッドは、アルゴリズムのうち変化する場所を表す
- 継承する側でsuperを呼び出すようなコードを書くのは避ける。代わりにフックメッセージを使う。
8章
- コンポジションとは、組み合わされた全体が、単なる部品の集合以上となるように、個別の部品を複雑な全体へと組み合わせる(コンポーズする)行為
- コンポジションにおいては、より大きいオブジェクトとその部品が、「has-a」の関係によって繋げられる
- 個々の部品(part)は「ロール(役割)」であり、自転車自体は、そのロールを果たすほかのどんなオブジェクトとも喜んで協力する
- コンポジションは、「hasa」関係を持ち、かつ、包含される側のオブジェクトが包含する側のオブジェクトから独立して存在し得ないものを指す -集約は、まったくもってコンポジションのようなものですが、一点、包含される側のオブジェクトの存在が独立していることが異なる
- クラスによる継承もコンポジションも「コード構成のテクニック」
9章
- 変更可能なコードを書くことに必要な3つのスキル
- オブジェクト指向設計の理解
- コードのリファクタリングに長けていること
- 価値の高いテストを書く能力(この章の主題)
- テストをすることの真の目的は、設計の真の目的がまさにそうであるように、コストの削減
- テストから優れた価値を得るには、意図の明確さが求められる
- テストの意図
- バグを見つける
- 仕様書となる
- インターフェースに依存したテストを作ることで内部設計に関する決定を遅らせる
- 適切なテストがなければ抽象度の高いコードベースの改修は厳しい
- 設計の欠陥を明らかにする ... テストのセットアップに苦痛が伴うのであれば、コードはコンテキストを要求しすぎている
- テストの意図
- 何を、いつ、どのようにテストするかを知る必要がある
- 論理的に考えれば、パブリックインターフェースに定義されるメッセージを対象としたものを書くべき
- テストは、オブジェクトの境界に入ってくる(受信する)か、出ていく(送信する)メッセージに集中すべき
- オブジェクトは、自身のパブリックインターフェースを構成するメッセージに対して「のみ」、状態についての表明を行うべき
- 送信メッセージには、副作用がまったくないもの(クエリ)と副作用があるもの(コマンド)がある
- クエリメッセージは、受け手のパブリックインターフェースの一部であり、必要な状態テストはすべてそちらで実装している
- コマンド(命令)メッセージが適切に送られたことを証明するのは送り手のオブジェクトの責任
- テストを最初に書く意味があるのならば、いつでもそうすべき
- 適切な時間に、適切な量で終われば、テストをすることと、テストファーストでコードを書くことは、全体的なコストを下げる
- テスティングのメインストリームに居ることには多くのメリット
- テストから優れた価値を得るには、意図の明確さが求められる
- 受信メッセージをテストするときは
- 使われていないメソッドを見つけたら削除する
- パブリックインタフェースをテストする
- テスト対象以外のオブジェクトをテストしなくていいようにオブジェクトが利用しているパブリックインタフェースを持ったロールを導入して依存オブジェクトをテスト用の簡素なもの(テストダブル)にする
- テストダブルを使うと、本番のコードが壊れているのにテストは通る場合があるが本番のコードを作るコストとの兼ね合いで使い分ける
- プライベートメソッドをテストするときは
- テスト中ではプライベートメソッドを無視する
- プライベートメソッドをそもそももたない(持ちすぎない)ようにする(そのようなクラスは単一責任の原則から外れている匂いがする)
- 複雑なコードをリファクタリングする場合など意味がある場合に限ってプライベートメソッドのテストを書く
- 送信メッセージをテストするときは
- クエリメッセージはメッセージの受け手のパブリックインタフェースとしてテストされるので無視する
- コマンドメッセージは送信したオブジェクトの責任としてテストをする
- メッセージが問題のオブジェクトに送信されたかどうかはモックを使ってテストする
- ロールの担い手が共有できるテストを書くには
- (MiniTestの場合)モジュールでインターフェースに応答するかどうかのテストを書き、includeでロールの担い手間でテストを共有する
- テストダブルと本番アプリケーションのクラスの間に同様のテクニックを用いて、インターフェースが変更された場合にテストダブルを利用している箇所が壊れていることを検出できるようにする
- ロールの担い手にロールの利用者側からメッセージが送信されているかどうかにはモックを用いてテストを行う
- (MiniTestの場合)モジュールでインターフェースに応答するかどうかのテストを書き、includeでロールの担い手間でテストを共有する
- 継承されたコードをテストするには