[オブジェクト指向]メッセージ基点の設計で柔軟性を高める

Tech

こんにちは。
お久しぶりです。浅野です。

最近、「オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方」という本を読みました。いくつか新しい発見がある中で、「設計において重要なのは、ドメインオブジェクトよりもメッセージだ」という主張が印象に残ったので、アウトプットしていきたいと思います。

本の紹介

今回読んだ「オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方」という本は、オブジェクト指向設計についての重要なエッセンスが分かりやすくまとまった一冊です。タイトル通りサンプルコードがRubyで書かれており、非常に理解しやすかったです。

前提

本書によると、オブジェクト指向設計の目的は、「あとにでも」設計できるようにすることです。
そしてオブジェクト指向設計とは、「依存関係を管理すること」だと主張します。変更を許容できるようなかたちでオブジェクトの依存関係を構成することで、未来の新たな要求にも柔軟に対応できるようになります。

本題

本書の主張では、「設計においては、ドメインオブジェクトよりもメッセージが重要」とされています。ドメインオブジェクトに集中しすぎると、オブジェクトに不自然な振る舞いを強いることがあります。しかし、メッセージを起点に設計を考えることで、必要なオブジェクトを特定し、適切な振る舞いを実現することができます。

事例

本書では、ある自転車旅行会社の事例をもとに説明されています。
旅行には、参加者の上限と体力に応じた難易度が設定されています。自転車は持ち込みも可能ですが、旅行会社から借りることもできます。
今回は、「参加者が、適切な難易度の、特定の日付の、自転車が借りられる旅行の一覧を見たい」というユースケースについて考えていきます。

必要なドメインオブジェクト

上記の例では、以下のドメインオブジェクトの必要性が見えてきます。

参加者(Customer)
旅行(Trip)
行程(Route)
自転車(Bicycle)

それでは、これらのオブジェクト同士がどのようなやりとりをしていくのか考えていきたいと思います。

シーケンス図の利用

著者は、オブジェクト同士のメッセージを考えるのに最適な方法が「シーケンス図」を書くことだと主張します。これにより、どのオブジェクトがどんなメッセージを送受信するかが明確になります。
例えば今回のユースケースでは、CustomerであるAliceとTripは、以下のメッセージのやりとりを行うことが想定できそうです。


ここでは、CustomerであるAliceがTripに向けてsuitable_tripsというメッセージを送っています。このメッセージはon_date, of_difficulty, need_bikeという3つのパラメータを要求します。
これはAliceがTripクラスに適切な難易度の、特定の日付の、自転車が借りられる旅行を見つけてくれることを期待しています。

誰がメッセージに応答すべきか

しかし、ひとつ違和感を覚えます。Tripは日程と難易度によって旅行を見つける責任を負うのは妥当に見えますが、自転車が利用可能かどうかを調べる責任を負うべきでしょうか?Tripの関心領域ではないように思われます。
では、誰が適任でしょうか?先程考えたドメインオブジェクトからはBicycleクラスの方がより適切に見えます。
その場合のシーケンス図は以下でしょうか。

今回の改善によって、Tripから余計な責任を取り除くことができました。しかし単にそれをCustomerに移しただけとも言えます。Aliceは適切な旅行を見つけたいだけにも関わらず、その旅行が具体的にどのようなものか(例えば、自転車が借りられる旅行か)まで知ってしまっています。つまり、Aliceが必要以上にシステムの内部構造を理解しているということです。これではあまりいい設計とは言えません。

必要なオブジェクトを見つける

これまでCustomerが送るsuitable_tripsメッセージに対する適切な受け手を考えてきましたが、既存のドメインオブジェクトからは見つけることができませんでした。新しいオブジェクトの導入が必要そうです。その新しいクラスには、適切な旅行を見つけるという責任を担ってほしいため、TripFinderと名付けましょう。シーケンス図は以下となります。

TripFinderは、適切な旅行の条件に関する知識を全て持ち合わせ、このメッセージに応答するために必要なことを何でも行います。さらに、安定したパブリックインターフェースを提供する一方、変化しやすい内部実装の詳細は隠蔽しています。
いまや、TripFinderはCustomerから独立し、他のオブジェクトからも利用可能となりました。将来他の旅行会社からWebサービスを介してTripFinderを利用したいという要求が出てきた場合にも、柔軟に対応できます。このアプローチは、システムの拡張性と柔軟性を大幅に向上させています。

まとめ

今回はメッセージ中心のアプローチを取り、アプリケーションの設計に取り組んできました。「どのオブジェクトがこのメッセージに応答すべきか」という視点で考えた結果、本当に必要なオブジェクトを特定することができました。オブジェクトは存在するからメッセージを送るのではなく、メッセージを送るためにオブジェクトが存在するのです。シーケンス図は、このプロセスでメッセージに集中することを助けてくれました。
もしドメインオブジェクトにのみ焦点を当てていた場合、今回のような解決策は見逃されていた可能性が高く、オブジェクトに無理な振る舞いを強いた複雑な設計になっていたかもしれません。メッセージを中心とした設計により、システムの構成要素間の関連を適切に把握し、より効率的で理解しやすいものにすることができます。

参考

Sandi Metz, “オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方”

タイトルとURLをコピーしました