woshidan's loose leaf

ぼんやり勉強しています

オブジェクト指向設計実践ガイド ~Rubyでわかる進化し続ける柔軟アプリケーションの育て方の4~6章を読んで

上記の本の4~6章を読んで印象に残った部分をまとめました。

4章

  • オブジェクト指向アプリケーションは「クラスから成り立つ」のですが、メッセージによって「定義される」
  • インタフェースには次の二種類がある
    • クラス内にあるインタフェース
      • そのうち、他のオブジェクトから使われるメソッドから成るパブリックインタフェース
    • 複数のクラスにまたがり、どのクラスからも独立しているもの(仮想のクラスのようなものを作る)
  • クラスのパプリックインタフェースの特性
    • クラスの主要な責任を明らかにする
    • 外部から実行されることが想定される
    • 気まぐれに変更されない
    • 他者がそこに依存しても安全
    • テストで完全に文書化されている
  • クラスのプライベートインタフェースの特性
    • 実装の詳細に関わる
    • ほかのオブジェクトから送られてくることは想定されていない
    • どんな理由でも変更され得る
    • 他者がそこに依存するのは危険
    • テストでは、言及さえされないこともある
  • ドメインオブジェクト
    • アプリケーションにおいて、「データ」と「振る舞い」の両方を兼ね備えた「名詞」を表すクラス
    • 大きくて目に見える現実世界のものを表し、かつ最終的にデータベースに表されるもの
    • こだわりすぎると設計が無茶なものになってしまう。オブジェクトそのものよりオブジェクト間のメッセージに注目する
  • オブジェクトとメッセージについて見当をつけるためのものとしてシーケンス図がある
    • シーケンス図を書くことでメッセージ交換の様子があらわになり「この受け手はこのメッセージに応える責任を負うべきだろうか」という疑問が湧くようになる
    • クラスの責任を考える議論からメッセージを決めどこに送るかの議論になっている
    • シーケンス図を書くことで責任を持ちすぎているクラスやメッセージの受け手となるクラスの存在が明らかになる
  • どうやって望むかではなく何を望むかによってメッセージを決めるとパブリックインタフェースが小さくなる
  • コンテキストはあるクラスを利用する際、どうしても必要になるコード
    • 依存オブジェクトの注入で相手が誰かを知らずにクラスの共同作業ができるようになるのでコンテキストが小さくなる
    • 他のオブジェクトを信用することでコンテキストに縛りつけずに作業できるようになる
  • インタフェースを作るコツ
    • パブリックインタフェースを作るときは
      • 明示的にパブリックインタフェースとわかるようにする
      • 「どのように」よりも、「何を」になっている
      • 名前は、考えられる限り、変わり得ないものである
    • プライベートインタフェースを作るときはprivate修飾子や_などの接頭辞でそれとわかるようにする
    • ほかのクラスと協力する際は、それらのパブリックインターフェースのみを使って、ベストを尽くす
      • どうしてもプライベートインタフェースに依存する場合は、メソッド内に依存を隔離する
    • パブリックインターフェースを構築する際は、そのパブリックインターフェースが他者に要求するコンテキストが最小限になることを目指す
  • デメテルの法則はオブジェクトを疎結合にするためのコーディング規則
    • 「直接の隣人にのみ話しかけよう」だとか、「ドットは1つしか使わないようにしよう」
    • デメテルの法則違反による列車事故は、パブリックインターフェースが欠けているオブジェクトがあるのではないか、というヒント

5章

  • ダックタイプはいかなる特定のクラスとも結びつかないパブリックインターフェース
    • クラスへの高コストな依存が、メッセージへのより寛容な依存で置き換えられる
  • Rubyにおける、オブジェクトの振る舞いについての一連の想定は、パブリックインターフェースへの信頼というかたちで行われる
    • もしあるオブジェクトがほかのオブジェクトの型を知っていれば、どのメッセージにそのオブジェクトが応答できるかを知っていることになる
    • クラスは、オブジェクトがパブリックインターフェースを獲得するための1つの方法でしかない
    • ダックタイピングはクラスに囚われないパブリックインターフェイスの定義の仕方で注意深く利用する必要がある
  • あるメソッドで具体的なクラスのインスタンスを受け取り、それも引数に異なるクラスを受け取る場合
    • それぞれのクラス名を見てメソッド名を具体的に探しにいくと依存が爆発的に増加してしまう
    • 一つのメソッドの目的は一つなのだからその引数も一つの目的を達成するために渡されてくると考える
    • 既存のクラスを引数にそのまま使うのではなく(オブジェクトのクラスについての不明瞭さを大目に見る)、クラス間にその目的に沿うパブリックインタフェースを定義する
    • ダックタイプをうまく定義できると、その後の拡張が簡単になる
  • オブジェクト指向プログラミングでのポリモーフィズムは、多岐にわたるオブジェクトが、同じメッセージに応答できる能力
    • ポリモーフィックなメソッドは「送り手の視点から見て」入れ替え可能であることに合意する
  • ダックタイプが必要であることに気づくことと、そのインターフェースを抽象化することのパターン
    • 次のものはダックで置き換えられる ... まだパブリックインターフェースを発見できていないオブジェクトを見逃している印
      • クラスで分岐するcase文
      • kind_of?とis_a?
        • 「メソッドがその引数のそれぞれから望むものは何だろうか」を問いかけて共通のパブリックインタフェースを用意する
        • Ruby on RailsRubyのコアクラスへの依存のようにダックタイプに直さなくても十分な例もある
      • responds_to?

6章

  • 継承を適切に使ったコードをどのように書くかの話
  • 継承とは、根本的に「メッセージの自動委譲」の仕組みにほかならない
    • 理解されなかったメッセージに対して、転送経路を定義するもの
    • 継承を宣言するだけで自動的に二つのクラス間で転送が行われるようになる
  • 具象クラスの中にstyleやtypeやcategpryで場合分けするクラスがあればその具象クラスの中に共通する要素はあるけれど異なる部分もある複数のクラスが埋め込まれている可能性が高い
    • その含まれるクラスが具象クラスの特化である場合は継承を使って解決できる可能性が高い
  • クラス間の関係を整理するのにUMLのクラス図が使える
  • 継承のルール
    • モデル化しているオブジェクトが一般-特殊の関係をしっかりと持っていること
    • 正しいコーディングテクニックを使っていること
  • 本で行われていた継承を使ったリファクタの手順
    • 一旦具象クラスだったスーパークラスをそのままサブクラスにして代わりに空の抽象クラスを作ってスーパークラスにする
      • コードをスーパークラスに昇格させるよりも、サブクラスに降格させるほうがかんたん
    • 全てのサブクラスに共通する振る舞いをスーパークラスに昇格させる
    • スーパークラスの初期値をメソッドで包んで継承したサブクラスに上書きする機会を与える
      • 「テンプレートメソッド」パターン ... スーパークラス内で基本の構造を定義し、サブクラス固有の貢献を得るためにメッセージを送るというテクニック
    • サブクラスにアルゴリズムを知ることを許し、superを送るよう求めるのではなく、スーパークラスが代わりに「フック」メッセージを送るようにする
      • フックメッセージは、サブクラスがそれに合致するメソッドを実装することによって情報を提供できるようにするための専門のメソッド