頑張るときはいつも今

自称エンジニアのゴリラです。日々精進。

【Ruby】オブジェクト思考設計実践ガイドを読み始めた # 第4章

前回と同様に、オブジェクト指向設計実践ガイドの続きです。

概念的な物がとても難しい。。。_

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

序文では下記のようなことが書かれていました。

  • オブジェクト指向はただのクラスの集まりではない
  • オブジェクトの責任、オブジェクトの依存関係ではなく、メッセージにも着目しなければならない

2つ目のメッセージは、おそらくメソッドのことかなと思うのですが、どうなんでしょうか??

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

下の図のように、オブジェクト同士のメッセージのやりとりが構成されているとします。

f:id:wa_football_1120:20200408183716j:plain

この時、パターンAとパターンBどちらがより良い設計でしょうか。

パターンAは、任意のオブジェクトが任意のオブジェクトに対してメッセージを送れるような構成になっています。

あるオブジェクトを変更するとアプリケーション全体に影響がかかる、あまりよろしくない設計です。

対してパターンBは、メッセージのパターンに何らかの制約があるように見受けられます。

パターンAのようなアプリケーションにならないために、

何をどのように外部に晒すのか?と言うことについて考える必要があります。

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

インタフェースには、

  • 外部からメッセージを受け取ることができるパブリックインタフェース

  • 内部のみで利用するプライベートインタフェース

があります。

これらのインタフェースについて考えていきます。

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

クラスが外部に公開するメソッド(パブリックインタフェース)は次の特性を備えます

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

テストで完全に文書化されているってなるほどなと思います。

Railsでもどの程度メソッドをテスト対象に含めるべきかって、最初の方迷いがちだったので、

1種の判断材料になりますよね。

下にも書いてありますが、プライベートインタフェースだからテスト対象に入らないという訳ではないので、

ある程度の規模感と複雑性を持ってテストをすべきなのでしょうか。

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

クラス内のみで使われるメソッド(プライベートインタフェース)は次の特性を備えます

  • 実装の詳細に関わる
  • 他のオブジェクトから送られてくることを想定しない
  • どんな理由でも変更され得る
  • 他社がそこに依存することは危険
  • テスト項目に入らないこともある

ドメインオブジェクト

一応、書内では仮想的な呼び方になっています。

よく分からないですが、DDDとかだとまた違う解釈になっているのだと思います。

ドメインオブジェクトの特徴としては、

  • データ振る舞いの両方を兼ね備えた名詞
    • 例えば、ユーザ、記事とか
  • 永続化する

ドメインオブジェクトとして管理すべき物は、アプリケーション設計で比較的簡単に見つかりますが、

そこにこだわりすぎると、無理な振る舞いをしてしまいがちです。

オブジェクト間のメッセージに着目してここら辺の振る舞いを改善していくことが大事らしいです。

クラス図に執着するよりも、シーケンス図に目を向けるといような感じでしょうか。

「どのように」を伝えるのではなく「何を」を頼む

パブリックインタフェースが小さいと言うことは、他のところから依存されるメソッドが僅かしかないことを意味する

依存関係について学んだ際に、変更される可能性が低いもの依存すると言うセオリーがありました。

クラスも同様で、あるオブジェクトが依存するパブリックのインタフェースはなるべく変更されないように実装しなければいけません。

(と言うより、突然メソッド名変えましたって言われたら確かに誰でもキレそうだな〜)

下のシーケンスを考えてみます。

TripオブジェクトからMechanicオブジェクトに対して、bikeを引数にいくつかの処理を加えています。

この場合、bikeに対して処理が追加された場合に、同じような処理を呼び出している箇所全てを呼び出す必要があります。

f:id:wa_football_1120:20200408183828p:plain

この複数の処理を、Mechanicオブジェクトに責務を持たせるようにします。

f:id:wa_football_1120:20200408183853p:plain

このようにすることで、bikeに対して必要な処理が増えたとしても、Mechanicのみを修正すればいいので、

メンテナンス性とコードの柔軟性が向上させることができるようになるようです。

コンテキストを最小限にする

ここで言う、コンテキストはメソッドの引数とかで使われるcontextとは別物だと思います。

メッセージを送るオブジェクトが求めているものというかもっと概念的な物な気がする(理解できるようになりたい)

パブリックインタフェースを構築する際には、このコンテキストを最小限にする必要があります。

メッセージの送り手が、送り先のクラスがどのように振舞いを実装しているか

知らなくても使えるようにしてくれということです。

(確かに、なんか使えそうなメソッドがあった際に、中身の実装を細かく分析するのは辛そうです。)

使うメソッドがどのように振舞うのかではなく、何をするだけを考えるだけで使うことができれば、

それは再利用ができているメソッドになります。

デメテルの法則

デメテルの法則はオブジェクトを疎結合にするためのコーディング規則の集まりです

いわゆる、異なるオブジェクト間のメッセージチェーンは繋げすぎないという法則です。

例えば、userとpostとcommentとcomment_attachmentfileがあるとして、

こんな感じの繋げ方は正直NGです。

user.posts.first.comments.attachmentfiles.url.underscore!

userとは遠くにあるオブジェクトをみているということになります。

なぜいけないのか?

userからattachmentfilesにたどり着くまでに、

postsとcommentsを経由しています。

この中間にいるオブジェクトの変更さえも動作に影響する可能性が出てくるためです。

ただし中間のオブジェクトが全て同じ方をもつ場合には、デメテルの法則違反にはならないことは覚えておく必要があります。

違反しない例

hash.keys.sort.join

まとめ

すんなり理解できたとは言えないですが、

設計のときは下のことも意識する必要がありそうです。

  • メッセージに着目する
    • メッセージの送り先が送り手の振る舞いに依存していないか
    • ドメインオブジェクトの中間に存在するオブジェクトが存在しないか
  • パブリックインタフェースは変更されにくいように設計する
  • 明示的なインタフェースを作る
    • どのように振舞うかではなく何をするになっている