|
||||
前回までで、クラスは1つの責務を持つべきである、というSRP
(Single Responsibility Principle)を紹介し、その際、クラスの「名前付け」
が非常に重要である、ということに触れました。 そして、名前にまつわるエピソードとして、JTP(Joshua Tree Principle)と "Name and Conquer"について話しました。 今回は、どうやってクラス間の結合を減らすか、ということについて考えてみ たいと思います。 クラスがさまざまな他のクラスと関連を持ち始めると、そのクラスが「太って」 しまうことがよくあります。これを、"fat interface"とか、アンチパターンの 言葉で"The Blob"(肥満児)と呼びます。つまり、関連するクラス毎にメソッド が多くなり、このクラス用のメソッド群と、このクラス用のメソッド群と、と いう具合にどんどんクラスが肥大化してしまう。さらに悪いことに、これらの 関連するクラス群すべてが、この大きなクラスに依存関係を持ってしまうので す。 この肥満児クラスに変更が起こると、その変更はいたるところに伝播し、シス テム全体に渡る修正インパクトが出てしまします。これをなんとか避けたい、 というのが動機です。 ISP(Interface Segregation Principle)は、Robert C. Martin(*1)による原則 で、 クライアントは自分が使わないメソッドに依存することを強制されない。 Clients should not forced to depend on methods that they don't use. というものです。クライアントが本当に必要としているインターフェイスのみ が、クライアントから見えるべきで、他のメソッドには依存したくない。依存 を最小にして、変更の伝播を最小限に食い止めたい。Segregationとは分割、分 離、という意味です。つまり、ISPは「インターフェイスをクライアント毎に分 離しよう」という原則なのです。 では、どのようにインターフェイスを分離するか。例えば、「Buttonが押され たらDoorが開く」、というアプリケーションを考えてみましょう。素直にやれ ば、ButtonクラスのbuttonPressed()というコールバックメソッドから、 Doorクラスのopen()メソッドを呼ぶ、という実装が考えられるでしょう。し かし、この戦略は、ボタンとドアの両クラスを直接依存させてしまします。本 来、ボタンという抽象とドアという抽象は独立しており、これらの間にはほと んど関係がありません。 1つのISP的な解決策は、ButtonListenerというボタンの押下イベントを受け 取るインターフェイスを作成し、Doorクラスがこのインターフェイスを実装す る、というものです。これは、「インターフェイスによる分離」という方法で す。こうすることで、ButtonクラスはButtonListenerというインターフェイ スのみに依存し、Doorクラスには依存しなくなります。 また、さらに「Timerがタイムアウトしたら、Doorがロックされる」という要求 があれば、ここでも、Timerクラスから直接Doorのlock()メソッドを呼ぶのでは なく、TimerListenerというタイムアウトイベントを受け取るインターフェイ スを作成し、Doorクラスがこのインターフェイスを実装する。 UMLで記述すると、こんな感じになります。 こうすることで、ButtonやTimerが直接Doorと依存しなくなります。しか し、こうしてもDoorの肥大化は避けられません。さらにもう一段進んだ分離 は、ButtonとDoorの間にButtonDoorAdapterクラスを、TimerとDoorの 間にTimerDoorAdapterクラスを挟むことです。これを「委譲による分離」と 言います。こうすれば、ButtonDoorAdapterクラスがButtonListenerを実装し、 そのbuttonPressed()の中からDoorクラスのopen()を呼び出す、という構造 になり、Doorという抽象を汚すことなく、DoorとButtonの実行時の連携を 実現できます。 * * * なるべく依存関係を減らすこと。これが、クラス間の結合度(Coupling)を減ら すポイントになります。その考え方として、ISPという原則と、「インターフェ イスによる分離」および「委譲による分離」という戦略があります。 ちなみに、イベントを受け取るインターフェイスとしてのListener(リスナー) というinterfaceは、Javaがイベントモデルを定義してから非常によく使われる 命名となってきており、この流儀はC#にも受け継がれています。現在、Javaや C#では半ば常識的となっている手法です。 |
||||
[1] "Agile Software Development"、Prentice Hall、2003 |
||||
つづき |