「オブジェクト指向設計実践ガイド」 第4章 柔軟なインターフェースをつくる

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

オブジェクト指向アプリケーション≠クラスの集まり。オブジェクト指向アプリケーションはクラスから成り立つが、メッセージによって定義される。このメッセージのやりとりはインターフェースを介して行われる。

インターフェース

インターフェースという言葉にはいろいろな意味が含まれるが、ここでは「クラスがどんなメソッドを実装していて、それらをどのように・どれくらい外部に公開しているか」という意味。(Javaのコードとして存在する「インターフェース」とは異なる。)

どのオブジェクトも自分の実装するメソッドを自由に他のオブジェクトに使われていたら、変更の影響は大きくなるだろう。どのメッセージがどのオブジェクトに渡せるかが定まっていれば、変更の影響は局所的に留めることができる。そのようなアプリケーションは、着脱可能でコンポーネント化されたオブジェクトから構成されているだろう。

インターフェースの定義

パブリックなインターフェースとプライベートなインターフェースはそれぞれ次のような特性がある。

パブリックインターフェース

  • クラスの主要な責任を明らかにする
  • 外部から実行されることが想定される
  • 変更されにくい
  • 他者がそこに依存しても安全
  • テストで完全に文書化されている

プライベートインターフェース

  • 実装の詳細に関わる
  • 他のオブジェクトからは見えない
  • 容易に変更されうる
  • 他者がそこに依存するのは危険
  • テストの対象でないこともある

つまり、パブリックインターフェースは「何をするか」、プライベートインターフェースは「どうやってするか」を表している。他のオブジェクトは他のオブジェクトに対して何をしてほしいかを伝えるが、どうやってそれをしてほしいかには関心を持つべきではない。

「どのように」は変わりやすいが、「何」は変化しづらい。あるオブジェクトがコレクションをソートして欲しいと他のオブジェクトに頼むとき、ソートのアルゴリズムがいつの間にか変わっているかもしれない。でもそんなことは依頼者のオブジェクトにとってはどうでもよく、ソートされた結果さえ得られればよいのである。

パブリックインターフェース

パブリックなインターフェースを設計するのには、シーケンス図が役に立つ。「名詞」で表されるドメインモデルがどのようにメッセージをやりとりするかを視覚化できる。このシーケンス図に現れるのはモデルと、モデル間のメッセージ。つまりパブリックなメッセージだけである。この図に現れるということは、そのメッセージはパブリックなものであるべきということがわかる。

「このクラスは何をすべきなんだろう」ではなく、「このメッセージは誰が応答するべきなんだろう」という視点を持つことが大事!

インターフェースの設計技法

  • クラスを作るときは毎回インターフェースを宣言する。(パブリック、プライベートともに)
  • 他のクラスを利用するときはパブリックなものだけを使う(Rubyの場合、privateがついていても#sendなど呼び出す手段はある。でもやめよう)
  • コンテキストを最小限に

デメテルの法則

デメテルの法則とは、オブジェクトを疎結合にするための設計規則。

直接の隣人にのみ話しかけよう

つまり、

foo.bar.baz

のようにドットがチェインするのはよくない。なぜなら #bar の戻り値オブジェクトが #baz に応答できるということを foo が知ってしまっているから。

とはいえ、

['a', 'b', 'c', 'd'].reject { |char| char == 'd' }.map(&:upcase).join(', ')

のようなコードは合理的である。途中の型がわかりきっているし、しかもそれらは組み込みで(めったに)変化しない。

このデメテルの法則が破られている箇所が見つかったとすると、そこにはおそらく適切に設計されていないパブリックインターフェースを持つオブジェクトがいる。