web-dev-qa-db-ja.com

クリーンアーキテクチャにおけるインターフェース分離の原則

クリーンアーキテクチャの第10章で、Martinはインターフェイス分離原則の例を示しています。その例と彼の説明を理解するのに問題があります。

この例では、OPSというクラスを使用する3つの個別のユーザー(クラス)があります。 OPSには、op1、op2、およびop3の3つのメソッドがあります。これらはそれぞれ1人のユーザーのみが使用します(op1はUser1のみなど)。

Martinは、OPSに変更が加えられた場合でも、それらがすべてOPSに依存しているため、OPSに変更を加えると、他のクラスの再コンパイルが発生することを通知します。 (したがって、op2を変更すると、User1の再コンパイルが必要になります。)

したがって、メソッドごとに1つずつ、合計3つのインターフェースが必要であると彼は主張します。次に、OPSクラスはそれらすべてを実装します。ユーザーは自分が使用するインターフェイスのみを使用します。したがって、User1はInterface1のみを実装しています。

マーティンによれば、OPSでのops2の実装が変更された場合、User1は、たとえばUser1がop2を記述するインターフェイスを使用しないため、これにより、たとえばUser1の再デプロイメントが停止します。

私には疑問があり、いくつかのテストを行いました。 (Martinは明示的にJavaを使用したので、私も同様に使用しました。)インターフェースがなくても、OPSを変更してもユーザーは再コンパイルされません。

そして、そうしたとしても(私はそう思っていました)、3つのインターフェイスを使用して同じクラスに3つすべてを実装させることは、私にとっても意味がありません。そのクラスの変更では、すべてのユーザーがインターフェースを再コンパイルする必要があるのではないでしょうか?コンパイラーは、変更した場所を分離して、変更したメソッドを記述するインターフェースに依存するユーザーのみを再コンパイルするのに十分スマートですか?ちょっと疑わしいです。

この原則が私にとって意味のある唯一の方法は、OPSクラスを3つの異なるクラス、インターフェース、またはノーに分割する場合でした。それは私には理解できましたが、それは明らかにマーティンの答えではありません。

どんな助けでも大歓迎です。

5
zalaponia

その特定の例では、Javaを使用すると、実際にインターフェースの利点が隠されます。インターフェース(または、より一般的にはlate binding)は、依存関係。

Direct dependency    Indirect dependency
=================    ===================

+---------+              +-----------+
| Service |              | Interface |
+---------+              +-----------+
     ^                     ^        ^
     |                     |        |
 +--------+          +---------+ +--------+
 | Client |          | Service | | Client |
 +--------+          +---------+ +--------+

ただし、Javaはすでに遅いバインディングを使用しています。Javaコンパイラが生成する.classファイルは、意味のある方法でリンクされていません。代わりに、 C++の意味でのこの種のリンクは、JVMが.classファイルをロードする実行時の後半に発生します。そのため、Javaのコンパイルモデルに焦点を当てることは、ここでは誤解を招きます。意味のないインタプリタ言語の実装。

明確にするために:ポリモーフィズム(およびISPをより慎重に適用)によって直接の依存関係を壊すことは、事前コンパイルシステム(ほぼすべてのC++実装など)のコンパイル時間に大きな影響を与えます。これは、pImplなどのC++固有のイディオムにも関連しています。

はるかに普遍的なISPの利点は、不要な依存関係を回避することにより、システムが

  • 理解しやすい;
  • テストが簡単。
  • 簡単に変更できます。

不要な依存関係の1つは、特定のサービス実装へのクライアントの依存関係です。これは、ポリモーフィズム/インターフェースを介して回避できます(依存関係の逆転の原則と比較してください)。もう1つの不要な依存関係は、インターフェースの追加メソッドに対するコンシューマーの依存関係です。インターフェイスを小さく分離した状態に保つと、これらの余分な依存関係が最小限に抑えられます。

私にとってISPからの主な要点は

  1. インターフェイスは、オブジェクトの実装方法ではなく、オブジェクトの使用方法使用によって定義する必要があります。使用の時点でインターフェイスを定義し、このインターフェイスを実装することをお勧めします(おそらく既存のクラスへのアダプタを実装することにより)。
  2. 小さなインターフェースは、基本的にはインターフェースに適用される単一責任原則です。
12
amon

インターフェースを「実装」していなくても、クラスのパブリックメンバーは暗黙的なインターフェースを形成することに注意してください。

私には疑問があり、いくつかのテストを行いました。 (Martinは明示的にJavaを使用したので、私も同様に使用しました。)インターフェースがなくても、OPSを変更してもユーザーは再コンパイルされません。

これは、OPSのパブリックインターフェイスを変更しなかった場合、つまり、実装の詳細のみを変更した場合にのみ当てはまります。パブリックメソッドのシグネチャを追加、削除、または変更した場合、既存のコンシューマーが破壊される可能性があり、優れたビルドツールはユーザークラスを再コンパイルします。

そのクラスの変更では、すべてのユーザーがインターフェースを再コンパイルする必要があるのではないでしょうか?

クライアントがインターフェイスにのみ結合されている場合、インターフェイスが変更されない限り、クライアントを再コンパイルする必要はありません。 OPSがインターフェイスを正しく実装しない方法で変更されると、ユーザークラスではなくOPSでコンパイルエラーが発生します。

この原則が私にとって意味のある唯一の方法は、OPSクラスを3つの異なるクラス、インターフェース、またはノーに分割する場合でした。

クラスは「何かがどのように実装されるか」、インターフェイスは「私がそれで何ができるか」と考えてください。実装が3つのインターフェースすべてのサービスを提供する場合、単一のクラスが3つのインターフェースを実装することは理にかなっています。その場合、3つのクラスに分割しても意味がありません。

3
casablanca