オブジェクト指向の設計方法を学んでいる。 今回は、「オブジェクト指向設計実践ガイド」の「第4章:柔軟なインターフェースをつくる」を読んだ。 なお、使用する言語はPython。
(過去記事)
aisinkakura-datascientist.hatenablog.com
aisinkakura-datascientist.hatenablog.com
aisinkakura-datascientist.hatenablog.com
4.1 インターフェースを理解する
この節に書いてあることは、クラス間で任意のメッセージをお互いに送れるようにするなということ。クラス間で送れるメッセージを制限し、クラスが外部に晒すメソッドやアトリビュートと内部でしか利用しないものとをしっかり分けておくべき、と書かれている。
4.2 インターフェースを理解する
クラス内のメソッドやアトリビュートのうち、外部から使われることを想定されているものはパブリックインターフェースと呼ばれる。また、パブリックインターフェース以外のメソッドやアトリビュートをプライベートインターフェースと呼ぶ。
パブリック・プライベートインタフェースの特徴は以下の通りである。
パブリック | プライベート |
---|---|
クラスの責任を表す | 実装の詳細に関わる |
クラスの外から利用される | クラスの中でしか使われない |
気まぐれに変更されない | 変更しても大丈夫 |
他のクラスがそこに依存しても安全 | 他の」クラスがそこに依存したら危険 |
テストで完全に文章化されている | テストでは実行されないこともある |
なお、Pythonの場合アンダーバー(_)が先頭に2個並んだメソッド・アトリビュートはプライベートとなる。 下記スクリプトを見ると、__をつけたprivateなメソッドの外からの実行や、アトリビュートを外から呼び出そうとするとエラーを出力してしまうことがわかる。
4.3 パブリックインターフェースを見つける
本書では、シーケンス図を用いてパブリックインターフェースを見つける方法が推奨されている。シーケンス図を用いることでクラス間のメッセージを明確にできるからだ。
ここで、ちょっとシーケンス図について説明したい。上記で述べたようにシーケンス図には、「メッセージ」という概念がある。例えば、「クラスAがクラスBにfというメッセージを送る」とは、「AがBのfメソッドを呼び出す」という意味である。
これをシーケンス図にしたのが図1である。
また、図1のシーケンス図をスクリプトにすると下記のようになる。
スクリプトを見るとわかるが、シーケンス図上で左にあるクラス(今回はclass A)は、右にあるクラス(今回はclass B)のインスタンスを持つことになる。私自身は、このメッセージとスクリプトの関係性がよくわからず理解に時間がかかってしまった。
この後は本書記載の図4.5~図4.8を実際にスクリプトに落とし込んでいこうと思う。
図4.5のスクリプト
図4.5は比較的単純である。イメージとしては、Tripクラスが自転車の用意の仕方まで完璧に把握していて、Mechanicクラスに手取り足取り指示しているイメージである。
図4.6のスクリプト
図4.5だと、Tripクラスが自転車の用意の細かい手順まで把握していないといけない。したがって、自転車の準備の仕方が変更になった場合、MechanicクラスだけではなくTripクラスにも変更が生じる。 これは良くないということで、Mechanicクラスに「自転車を準備しろ!」と命令を出せば、自転車の準備の各作業はMechanicがやってくれるようにしたのが、図4.6である。
図4.7のスクリプト
図4.6でMechanicに自転車の細かい準備をすることを任せることができた。しかし、依然としてTripは自転車を用意する命令を出す必要がある。これをただ単に旅行の準備をする命令に変えたい。その理由としては、Tripが必要な旅行の準備が自転車の準備ではなくなってしまった場合や、自転車の準備以外もしなければいけなくなったときのために拡張しやすくするためである。ここでのポイントは、Tripは自分の情報を全てMechanicに提供し、Mechanicはその中から必要な情報だけを取得してよしなにやる状態を目指すことである。
図4.8のスクリプト
図4.8はCustomerクラスとTripクラスの間にTripFinderクラスを入れて、旅行の依頼、旅行の準備、条件に合う旅行を見つける役割を全て分解した。
4.4 一番良い面(インターフェース)を表に出すコードを書く
本節に書いてあることは、ここまでのまとめである。重要なことは3点と考えた。
- パブリック・プライベートインターフェースが明示的にわかるように/_をつけたり、命名規則を作るなどすべき
- 過去に作成されたコードのプライベートインターフェースを外のクラスから使うようなことがあってはならない。使わざるを得ないときも、徹底的に代案を考える
- パブリックインターフェースを作る際は、メッセージを送信するクラスが「どのように」ではなく「何を」要求しているのかを意識する
デメテルの法則
デメテルの法則は、クラス間をつなぐドットは一つだけにしようという法則である。 例えば、コード中に下記のようなスクリプトが出てきたら、デメテルの法則違反である。
trip.mechanic.prepare_bicycle
このようなスクリプトが良くない理由、下記2点によるものである。
- 変更に弱い
- 再利用しにくい
1点目の「変更に弱い」について。上記のスクリプトがずっと使えるためには、mechanicとprepare_bicycleの名前が変わらない必要がある。逆に言えば、mechanicとprepare_bicycleの片方でも名前が変われば、このスクリプトをは動かなくなる。そのため、変更に弱い。
2点目の「再利用しにくい」について。上記のようなスクリプトは、「tripがmechanicを持っている」「mechanicがprepare_bicycleを持っている」こと2点を要求している。したがって、tripを再利用しようとしたら、mechanicとmechanicが持つprepare_bicycleも併せて再利用する必要がある。これはしんどい。
以上のことから、ドットは一つにすべきというのが「デメテルの法則」である。なお、変更がほとんど生じないであろうオブジェクトやメソッドに依存する場合(基幹的なパッケージなど)は、多少デメテルの法則を違反しても良いと本書には書いてあった。
まとめ
本章の学びは以下の通り。