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

Tony Duong

Tony Duong

7月 15, 20261

他の言語:🇫🇷🇬🇧
#poodr#ruby#object-oriented-design#interfaces#law-of-demeter#sandi-metz
POODR 第4章:柔軟なインターフェースをつくる

概要

最初の3つの章は、個々のクラスの設計 — オブジェクトが何を知っているか — に焦点を当てていました。第4章は、オブジェクトが互いに何を語り合うかへと視点を移します。アプリケーションは、そのクラスと同じくらい、メッセージのパターンによって定義されます。この章のテーマはインターフェースの設計です。すなわち、オブジェクトが受け取ることをいとわないメッセージのことです。

2種類のインターフェース

ここでいう「インターフェース」とは、オブジェクトが応答するメッセージの集合を意味します。どのクラスにも2つあります。

  • パブリックインターフェース — クラスの主要な責務を構成するメソッド。安定していて、他者が依存しても安全であり、徹底的にテストされています。これはクラスが世界に対して見せる「顔」です。
  • プライベートインターフェース — 内部の実装の詳細。予告なく変更されることがあり、他者から呼び出されることを意図しておらず、外部が書くテストに現れるべきではありません。

良いパブリックインターフェースは、クラスが何をするのかを明らかにしつつ、どうやってそれを行うのかを隠します。レストランのメニューを思い浮かべてください。注文できる料理は載っていますが、レシピは載っていません。

パブリックインターフェースを見つける:ドメインオブジェクト vs メッセージ

この章では「自転車ツアーを運営する」という例(Customer が特定の難易度で利用可能なツアーを見たい)を使います。まず明白なドメインオブジェクトCustomerTripBicycleMechanic — を列挙したくなります。これらの名詞は実在し、見つけやすいものですが、罠でもあります。名詞に注目すると、その振る舞いを本来持つべきクラスではなく、最初に目に留まったクラスに割り当ててしまうのです。

その代わりに、まずメッセージを設計しましょう。 シーケンス図を使って問いかけます。このメッセージが与えられたとき、誰が応答すべきか? これは「このクラスが必要なのは分かっている、では何をすべきか?」という問いを、「このメッセージを送る必要がある、では誰が応答すべきか?」へと反転させます。会話を設計することがオブジェクトを明らかにするのであって、その逆ではありません。

設計における鍵となる問いは「どんなオブジェクトが必要か?」ではなく、「どんなメッセージが送られ、誰がそれに応答すべきか?」である。

「どうやって」ではなく「何を」を求める

決定的な区別があります。メッセージは、オブジェクトに何を望むのかを尋ねる(そしてどうやるかは受け手に任せる)こともできれば、受け手がどうやって仕事をすべきかを指図することもできます。

TripMechanic に、各自転車を準備するための手順を正確に指示すると、Trip は整備士の手続きに結合してしまいます — 自転車の準備方法が変わるたびに Trip も変更を強いられるのです。その代わり、Tripprepare_bicycles(bicycles) のような意図を明らかにする単一のメッセージを送り、手順は Mechanic に決めさせるべきです。協力者を細かく管理せず、依頼を果たしてくれると信頼すること、それが良いオブジェクト指向設計の核心です。

コンテキストからの独立を求める

オブジェクトが期待するコンテキストとは、機能する前にその協力者について知っていなければならないすべてのことです。オブジェクトが必要とするコンテキストが多いほど、再利用やテストは難しくなります。

目標はコンテキストからの独立です。すなわち、何を望むかは知っているが、他のオブジェクトがどうやってそれを果たすのかは何も知らないオブジェクトです。依存性注入(第3章より)が主要な手段です — 「準備係」を手渡され、単に prepare_trip(self) を送るだけの Trip は、その準備係が整備士なのか、運転手なのか、料理人なのかを気にしません。各協力者が、必要なものをその旅(trip)に尋ねるのです。

デメテルの法則

デメテルの法則(Law of Demeter, LoD)は、メッセージを送ってよい相手を制限します。それは列車の衝突事故(train wrecks) — 1つのオブジェクトを貫いて遠く離れたオブジェクトと話そうとする長いメッセージの連鎖 — を禁じます。

customer.bicycle.wheel.tire      # violates LoD
customer.bicycle.wheel.rotate    # violates LoD
hash.keys.sort.join(', ')        # arguably fine — see below

くだけた言い方をすれば「すぐ隣の相手とだけ話せ」、あるいは「ドットは1つだけ使え」です。メソッドは self、引数として渡されたオブジェクト、そして自分が直接生成したオブジェクトにはメッセージを送ってよい — しかし、ある協力者の内部を掘り進めて別の協力者へ到達すべきではありません。

すべての連鎖が法則の精神に反するわけではありません。hash.keys.sort.join は連鎖ですが、各呼び出しは同じ種類のものを返し、無関係な遠くのオブジェクトに手を伸ばしていないため、結合のコストは低いのです。LoD が本当に警告しているのは、協力者の内部構造を露呈し — そしてそれに依存する — メッセージの連鎖です。

train wreck は症状です。治療法はドットを機械的に取り除くことではありません。その連鎖が欠けているメッセージを露呈していると気づくことです。最初のオブジェクトに、あなたが本当に欲しいものを尋ね、内部での委譲はそのオブジェクトに任せましょう。これによって、新しく有用なパブリックメソッドが姿を現すことがよくあります。

重要なポイント

  1. パブリックとプライベートのインターフェースを区別する何をするかを明らかにする安定したメソッドを公開し、どうやってを実装する変わりやすいメソッドは隠す。
  2. オブジェクトより先にメッセージを設計する — シーケンス図を使い、「このメッセージに誰が応答すべきか?」と問う。オブジェクトは会話から立ち現れる。
  3. ドメインの名詞ではなくメッセージに注目する — 明白なクラスは出発点であって、設計そのものではない。
  4. 「どうやって」得るかではなく、「何を」欲しいかを求める — 意図を明らかにするメッセージを送り、協力者を信頼する。
  5. コンテキストからの独立を求める ことでオブジェクトを再利用・テストしやすくする。依存性注入が主要な手段である。
  6. デメテルの法則に従う — 協力者を貫くメッセージの連鎖を避ける。train wreck はたいてい、欠けているパブリックメッセージのサインである。

🌐 Claudeによる翻訳

Tony Duong

著者: Tony Duong

デジタル日記。思考、経験、そして人生についての考え。