ravelll の日記

よしなに

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

5章はダックタイピングの話。Ruby の特徴としてよく語られますね。

勘所がつかめると設計が洗練されるだけでなく OSS プロダクトのコードリーディングが捗って良さそうだな〜と思いました。

動的/静的型付け言語の話はなかなか強い口調で語られていて、読んでいて少しヒヤヒヤするところがあった。

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

  • ダックタイプ:どの特定のクラスにも結びつかない(クラスをまたぐ)パブリックインターフェース
  • 「アヒルのように鳴き、アヒルのように歩くならば、それはアヒルである」

5.1 ダックタイピングを理解する

  • Ruby ではパブリックインターフェースを信頼を元にオブジェクトの振る舞いを期待する
    • 「あるオブジェクトの型を知っている」ことは「オブジェクトが応答できるメッセージを知っている」ということ
    • あるクラス A のオブジェクトと相互作用するオブジェクトに必要なことは「相手が A のインターフェースを実装していると信頼する」こと
  • オブジェクトの使い手はそのクラスを気にする必要がなく、気にするべきでない
    • 重要なことはオブジェクトが「何であるか」でなく「何をするか」
  • ダックタイプのパブリックインターフェースは契約を表す
  • Trip#prepare は Mechanic クラスには依存していないが「prepare_bicycles に応答できるオブジェクトを受け取る」ことに依存している
  • ダックタイプ化は少しの理解力と引き換えに拡張の容易さを提供する
    • 具象的なコードは理解易・拡張難、抽象的なコードは理解難・拡張易
  • オブジェクト指向におけるポリモーフィズム:種々のオブジェクトが同じメッセージに応答できる能力

5.2 ダックを信頼するコードを書く

  • クラスによって分岐する case 文は未特定のダックの存在を示唆する
    • 共同作業するオブジェクトについての知識を持ちすぎていて、信頼が欠けている
  • ダックタイプを実装するクラスは振る舞いもいくらか共有する必要がある
    • モジュールによってロールを共有する方法に続く
  • ダックタイプは時と場合を考えて使う
    • ダックタイプを見つけて実装してもアプリケーション全体のコストが下がらない場合もある

5.3 ダックタイピングへの恐れを克服する

  • コンパイラは不慮の型エラーからプログラマーを救うことはできない
    • 変数を新しい型にキャストできる全ての言語には脆さがある
  • 静的型付けによって安全になるという考えは幻想である
  • 現実の世界で、コンパイラで防げる実行時の型エラーはほぼ確実に起こらない
    • コンパイラの助けがなければ型エラーは起こる、という説に対して
  • 動的型付け言語は、高コスト低価値なコンパイル時の型検査と、コンパイル/make サイクルを除くことで得られる莫大な効率性とを交換してくれる
    • :fire:

5.4 まとめ

  • ダックタイピングはパブリックインターフェースを特定のクラスから切り離し、「何であるか」ではなく「何をするか」によって定義される仮想の型を作る

第110回 PHP 勉強会に行ってきた + LT してきた

phpstudy.doorkeeper.jp

久しぶりの PHP 勉強会。最後に行ったのは第96回で、調べてみたらもう1年以上も前のことだった。当時に比べれば随分と PHP に詳しくなったと思う。

LT は最近踏んだバグについて話してきた。そこそこ盛り上がって安心しました。

speakerdeck.com

勉強会全体としては、 @ex_takezawa さんの DDD の話で過去に自分がチームに DDD 導入しようとして爆死した思い出が蘇ったり、@kuwahara_jsng さんから自分に知見があまりない CodeIgniter4 の話が聞けたり、飛び入り参加で @tomzoh さんがめっちゃいい話されてたりで、短時間ながらとても楽しく得られる物があった会でした。

来月も何かいいネタがあれば LT 応募しようと思います、お疲れ様でした!

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

パブリックインターフェースについての話。デメテルの法則についても触れていました。

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

  • オブジェクト指向アプリケーションは「クラスから成り立ち」「メッセージによって定義される」
  • オブジェクトの責任 = オブジェクトが何を知っているか
  • オブジェクトの依存関係 = オブジェクトが誰を知っているか

4.1 インターフェースを理解する

  • オブジェクト同士が複雑に依存してしまうような設計の問題は、クラスが何を「する」かでなく、何を「明らかにする」か、の視点が欠けていることにある
  • 他のオブジェクトからの利用が意図されているメソッドが、そのクラスのパブリックインターフェースを構築する

4.2 インターフェースを定義する

  • レストランの厨房から見て、お客さんが使うことが期待されるパブリックインターフェースはメニュー
    • メニューを使うことで厨房が「どのように」料理を作るかを関知させず「何を」望むかをお客さんに頼ませることができる
  • パブリックインターフェースの特性
    • クラスの主要な責任を明らかにする
    • 外部から実行されることが想定される
    • 気まぐれに変更されない
    • 他者がそこに依存しても安全
    • テストで完全に文書化されている
  • パブリックインターフェースはクラスの責任を明確に述べる契約書
  • 「変更の可能性が低いところにのみ依存する」という考えはクラス内のメソッドにも当てはまる

4.3 パブリックインターフェースを見つける

  • ドメインオブジェクト = アプリケーションにおいてデータと振る舞いの両方を兼ね備える名詞を表すもの
  • 設計時にはドメインオブジェクトに集中はせず、オブジェクト間のメッセージに注意を向ける
  • オブジェクトとメッセージを実験するにはシーケンス図を使うと良い
    • パブリックインターフェースをあらわにし、実験し、定義するための手段となる
    • メッセージに集中することを助け、テストで最初に明らかにしたいことへの合理的な見当をつけられるようになる
  • メッセージに基づく視点はクラスに基づく視点よりも柔軟なアプリケーションを生む
  • オブジェクトが存在するからメッセージを送るのではなく、メッセージを送るためにオブジェクトは存在する
  • パブリックインターフェースが小さい = 他から依存されるメソッドがわずかしか無い

4.4 一番良い面(インターフェース)を表に出すコードを書く

  • インターフェースこそが全てのテストや他のどんなコードよりもアプリケーションを定義し将来を決定づける
  • プライベートメソッドは全くテストしない、もしくはパブリックなメソッドからは隔離する
    • 不安定なプライベートメソッドに依存するテストを書かない
  • 外部フレームワークへの依存は技術的負債の一形態である
  • パブリックインターフェースを構築するときは、他者に要求するコンテキストが最小限になることを目指す
    • メッセージの送り手が、クラスがどのように振る舞いを実装しているかを知ることなく、望むものを得られるようにする

4.5 デメテルの法則

  • デメテルは3つ目のオブジェクトにメッセージを送る際、異なる型の2つ目のオブジェクトを介すことを禁じる
    • 「直接の隣人にのみ話しかける」
  • 中間オブジェクトを介して遠くの属性を取得することが最も低コストな場合もある
    • 変更のコスト・頻度と、違反修正のコストを比べてバランスをとる
    • デメテルの法則を違反することで負うリスクは安定した属性に対しては低い
  • メッセージチェーンを除去する方法の1つは、メッセージを移譲すること
    • 結合を隠しただけで実際にはコードの結合が解かれていないような場合もある。移譲は注意して行うこと
  • メッセージに基づく視点でメッセージを見つけたなら、そのメッセージは何らかのオブジェクトのパブリックインターフェースとなる
    • そのオブジェクトが何のオブジェクトかはメッセージ自体が導いてくれる

4.6 まとめ

  • オブジェクト指向アプリケーションは、オブジェクト間で交わされるメッセージによって定義される
    • メッセージの交換はパブリックインターフェースに沿って行われる
  • メッセージが「受け手がどのように振る舞うか」を伝えるのではなく、相手を「信頼」し送り手が望むことを頼むものであるとき、柔軟なパブリックインターフェースを自然に進化させていく