うぃろぅ.log

140字で綴りきれない日々の徒然備忘録

【備忘録】 「オブジェクト指向設計実践ガイド」を読む

うぃろぅです。

今日は読書の方。学びを書いていきます。

今日の本

オブジェクト指向設計実践ガイド

Java版が有名だが、そのRuby版が出版されている。
Rubyの環境は会社のPCにも入っている(こそっと入れた、ただしirbが安定して動かない)ためお試しもできる。

第1章 オブジェクト指向設計

オブジェクト指向設計の理論。

少し読めば気がつくが、本文に意訳が少なく、英語で書かれた説明文っぽさが前面に押し出されている。
恐らく意識して訳者の意思を排しているのだと思われる。

この章は図説もソースコードもないため、大判の本に文字がこれでもかと並んでいて少し圧倒される。我輩が猫であるを思い出したりなどしていた。

第2章 単一責任のクラスを設計する

将来何が起こるかわからないため、とりあえず機能を分離する。
決定を遅らせることが重要で、そのために1つの処理で1つの機能、としておくと対応しやすくなる。

この本では自転車の例を挙げて説明している。オブジェクト指向界隈、自転車だったり車だったり乗り物と縁が深め。多分例に挙げやすいだけだと思うが。

それと52ページに誤植がある。

def diameter(wheel)
  wheel.rim + (wheel.tire * 2))
end

閉じ括弧が1つ多い。恐らく53ページ目のソースをコピペして消し忘れかなと。

第3章 依存関係を管理する

どうでもいいけれど「いそん」って読む人少ないよね。辞書的にはどちらでもいいしNHKも「イゾン」と読むように変更したとのこと。でも「いそん」の方がなんとなく響きが綺麗。それだけ。

GearクラスがWheelクラスの存在を知らなければならないソースになっているため、その依存関係を修正する。いわゆる疎結合

これはテストコードに関してもいえることで、コードを修正するたびにテストコードも修正しなければならないようなテスト設計はNGとなる。
言われてみればそのとおりだが、これまであまり意識してこなかった。

知識を減らすことで、より賢くなったのです。

「依存オブジェクトの注入」についての説明箇所だが、引用箇所の言葉回し好き。めっちゃお洒落。

依存関係に関しては

  • 変更の起こりやすさ
    自分のコードより開発の活発なフレームワークの方が変わりやすい等。
  • 具体クラス vs 抽象クラス
  • 依存関係が多いクラス

の大きく3点に気をつける必要がある。
上手いこと自分より変更が少ないクラスを見つけ、そこに依存しよう。

第4章 柔軟なインターフェースをつくる

クラス内をパブリックとプライベートに分ける重要性について。

さらには設計のポイントについて。
クラスを中心に考えるのではなく、メッセージを中心に考えられるようになることが設計者としてのキャリアの第一歩。

オブジェクト指向設計の要は手放しの信頼である。
「詳細はわからないけれど呼べばよしなにやってくれるはず」を繋げることで機能が成り立っていくことが拡張性のあるプログラムだという話。確かに単一責任であるということは他の機能に対して責任を負う必要がないということで非常に理にかなっている。

パブリックインターフェースに含まれるメソッドの条件は以下のとおり。

  • 明示的に特定できる
  • 「どのように」ではなく「何を」になっている
  • 名前は恒久的に変わらないものである
  • オプション引数としてハッシュをとる

「明確に」を合言葉に。複雑さはどこでも求められていない。

ここまでの説明を下地に「デメテルの法則」について検討。

ja.wikipedia.org

Wikiの説明でもがわかりやすかったため貼ってしまうが、要は「友達の友達にものを頼むな」ということである。

第5章 ダックタイピングでコストを削減する

「ダックのように鳴き、ダックのように歩くのであれば、それはダックである」というやつ。

共通して行っている機能を抽象化して切り出してあげることで、「何やっているかわからないけれどこのメソッドは実装しているはず」と信頼して処理を投げられる。
抽象メソッドみたいなものかな、という認識。ちょっと違うとは思うが。

じゃあ何がダックで置き換えられるのよ、ということで以下が置き換えられる例。

  • クラスで分岐するcase文
  • kind_of?is_a?
  • respond_to?

詳しい説明は書籍参照のこと。

また、ダックタイピングに関連して静的型付け言語対動的型付け言語の比較。
私はJavaC#を少し触ったことがあるが、Rubyの方がコーディングしていて楽しいから動的型付けでも全然問題ないと思っている。速度に対してシビアな状況に直面していないからかもしれないが。

第6章 継承によって振る舞いを獲得する

これまではコードの最小化がテーマ、ここからはコードの共有がテーマ。

継承を利用する際には「is-a関係」を意識してあげると上手くいくことが多い。もちろん全てにおいて有効というわけではないが、判断基準のひとつとしては有効なものである。

Javaではabstractキーワードをつけることでスーパークラスインスタンス生成を抑止できるが、Rubyにそのような制限をつけるキーワードはない。プログラマを信頼しているからね。

継承を実装するときは実際に継承が有効になってからで充分。YAGNIでいこう。

リファクタリングのポイントとして、既存のクラスをスーパークラスとサブクラスに分けたら、まずはサブクラスに全てのコードを移動してしまうということが挙げられる。
別のサブクラスを宣言したとして、エラーメッセージを見て「何を共有するか」を判断しやすいからである。なるほど。結果的にスーパークラスに余計な処理を入れずにすむと。

通化したからといってそれでいい、というものではなく。
superの呼び忘れは誰しも起こりえるのでそれに対する対策も取り込むことでより堅牢になる。そのための手段がフックメッセージであると。
これは知らなかった。積極的に取り入れていきたい。

第7章 モジュールでロールの振る舞いを共有する

継承可能なコードの書き方に続いて継承の使いどころさんを解説。

モジュールをmix inする関係の詳しい解説はチェリー本を読むといいと思う。

いつもお世話になっております。

継承との使い分けとして「である(is-a)」か「のように振る舞う(behaves-like-a)」かが1つの基準。
機能を共有することは継承と似ているので、フックメソッドを実装するのが吉。

第8章 コンポジションでオブジェクトを組み合わせる

オブジェクト指向コンポジションのテクニックについての解説。

パーツを組み合わせて全体を組み合わせることをコンポジションと呼んでいる。

クラスを構成する要素をパーツごとに分解して…となると知識量が多くなるため、ファクトリーパターンを活用する。

gihyo.jp

あとソースコードに誤植あり。ミスは誰にでもある。

コンポジションか継承かで迷った場合、明確なメリットがないのであればコンポジションを優先するべき。掘り下げるなら継承、パーツを集めるならコンポジション、といったところか。

第9章 費用対効果の高いテストを設計する

上手なリファクタリングのためには価値の高いテストが必須。
テストを書き直すことなくリファクタリングを進めていけるテストコードについての解説。

テストの利点として挙げられているのは以下。

  • バグ発見
  • 仕様書になる
  • 決定を遅らせる
  • 抽象を支える
  • 設計の欠点の明確化

詳細は書籍参照。

使われていないコードでも存在するのであればテストをしなければならないため、コードを削除することも大事。

受信メッセージをテストするためのテストダブルがスタブ、送信メッセージをテストするためのテストダブルがモックという認識。

抽象スーパークラスの機能をテストするために、テスト用のサブクラスを作ることも有効。

とりあえず手は動かしたが完全に理解できたかというと完全ではない。
引き続き手を動かしていきたいところ。

おわりに

オブジェクト指向わかってきたわ」と思ってこの本を読んだわけだが、わかった気になっていただけだったということが身にしみてわかった。

オブジェクト指向マスターへの道は遠く険しい。
まだまだ精進ですなぁ。

今回の手を動かした証跡は以下。

github.com

いい本だった。きっとまた読み直す日が来る。

ではまた。