web-dev-qa-db-ja.com

すべてのサービスクラスにインターフェースを持たせる意味は何ですか?

私が働いている会社では、すべてのサービスクラス対応するインターフェイスがあります。
これは必要ですか?

ノート:

  • これらのインターフェースのほとんどは単一クラスでのみ使用されます
  • パブリックAPIは一切作成していません

具象クラスをモックできる最新のモックライブラリと、2回程度のクリックでクラスからインターフェイスを抽出できるIDEを使用すると、これは以前の時代からの引き継ぎにすぎませんか?

111
Bob Roberts

あなたの会社はSOLID=の原則に従っており、具体的なクラスではなくインターフェースをターゲットにすると、プログラムにオーバーヘッドが追加されなくなります。彼らがしていることは、ボリュームを返済できる非常に適度な追加作業です。あなたがしている仮定は最終的には間違っています...そしてそれがどのような仮定になるかは決してわかりません。あなたが推奨することは少し作業が少ないのですが、リファクタリングに何時間も多くの時間を費やす可能性があります基本的なOO設計原則に従ってください。

あなたはこれらの人から学び、自分を幸運だと考えるべきです。しっかりとしたデザインを優先目標とするこのような環境で仕事をしたいと思います。

98
Edward Strange

あなたの質問を考えると、この種の設計の理由は文書化されていないと思います。

[〜#〜] yagni [〜#〜] に違反しているため、単一実装のインターフェースの不正な使用は明らかに間違っています。私の経験では、これはまた、主にインターフェイスを実装するメソッドが不必要にパブリックにされることを余儀なくされているため、メンテナンス面でかなり有害です。さらに多くの リファクタリング エクスペリエンスを収集した後、privateおよびpackage privateアクセス修飾子の控えめな魅力を認めることができるようになります単一のクラス/パッケージ内で注目を集める。

文書化されていないこのイディオムの正当な使用法については、それは私の目にはまずい-とにかくメンテナが「悪いことから良いことを伝える」ことができないからです。その結果、これはあまり魅力的でないオプションの間に1つを残します。オプションは、疑わしいパブリックなものを現状のまま維持することにより、繰り返しコードを変更する必要があるたびに生産性を低下させるか、単一の実装を盲目的に「折りたたむ」ことでリスクのある変更を行い、保守を容易にすることです。 [〜#〜] pojo [〜#〜] -自分をリスクにさらし、これが間違った方法であることを痛感すること、他のいくつかのこと(神は禁ずる remote )モジュールあなたの視界の外はインターフェースを期待しています。

私は、単一実装インターフェースの誤った崩壊によって引き起こされた「啓示」を何度か経験しました。毎回それは教育的な経験でしたが、私は本当に再びそこに行きたくありません。ことは、これはテストの後半、統合時、またはユーザーの受け入れ時に発生し、この段階でのずっと前の間違いをロールバック/修正するのは非常に面倒です。


  • 言及せずに残しておくべきではないオプションは、ドキュメント化されていない使用が正当な理由であるかどうかを調査することです(その後、それぞれ折りたたむかドキュメント化するか)。

    私にとって、これはかなり逆効果です。このアプローチは基本的に、フルラウンドの統合テストに行くことを強制します。これは、複雑な修正/リファクタリングの最中にbreak a flowを行う最も効率的な方法の1つです。

    http://i.stack.imgur.com/tBUBG.jpg

私は実用性を見ることができませんでした...インターフェイスはcom.company.service.foocom.company.service.foo.implのように1対1で使用されます

以下は、私がそのようなケースを通常どのように処理するかについてです。

  1. issue tracker でチケットを作成します(例:"PRJ-123文書化されていない、Foo/FooImplでの単一の実装によるインターフェースの不審な使用法)
    チケットの説明に追加するか、これが保守性を損なうという説明にコメントし、ドキュメントがないと overengineering のようになることを指摘します。これをPOJOに「折りたたむ」ことで問題を修正することを提案するコメントを追加して、保守を容易にします。
  2. 誰かが説明しに来た場合に備えて、しばらく待ってください。 少なくとも一週間か二週間待ちます

    • 誰かが目的を説明した場合は、a)それをチケットに追加します。b)Fooにjavadocsを追加しますpurpose PRIF-123で説明されているインターフェースの目的c)閉じるチケット、d)次の3つのステップをスキップします。
    • さもないと、 ...
  3. チームリードまたはマネージャーに来て、提案されたようにチケットを修正したらどうなるか尋ねます。
    提案された修正が間違っていることが判明した場合に備えて、「悲鳴テスト」に耐える十分な権限を持つアドバイザーを選んでください。
  4. 提案された修正に従ってPOJOに折りたたみ、可能な場合は コードレビュー を調整します(このような滑りやすいケースでは、背中を覆うのに害はありません)。
  5. 変更が完全なテストに合格するまで待ち、結果に応じてチケットを更新します。変更がテストに合格したかどうかの情報を追加します。
  6. パイロットチケット(PRJ-123)の結果に基づいて、残りの同様のケースに対処するために、新しい課題追跡チケットを作成して処理します。目的を文書化するか、POJOに折りたたみます。
84
gnat

Adam Bien この質問について考えてみましょう: Service s = new ServiceImpl()-なぜあなたはそれをしているのですか?

これは肥大化するだけでなく(「Convention Over Configuration」の考え方とは逆です)、実際にダメージを与えます。

  1. 別の実装を取得すると想像してみてください(それがインターフェースの要点です)-どのように名前を付けますか?
  2. アーティファクトの量が2倍になり、「複雑さ」が大幅に増加します。
  3. インターフェースとインプリでjavadocを読むのは本当に面白いです-別の冗長性
  4. IDEのナビゲーションは流暢ではありません

そして私は彼に同意します。

おそらくあなたの同僚はJ2EEに遅れをとっています。これはJ2EEで作業するときに必要だったため、おそらくJ2EEの困難な時期に関する古いチュートリアルをインターネットで見つけることができます。

40
Xoke

Javaインターフェースはモック性だけではありません(もちろん、それが要因です)。インターフェイスは、プライベートメソッドや属性などの実装の詳細なしで、サービスのパブリックコントラクトを公開します。

(あなたの場合のように)内部サービスを使用する場合の「パブリック」とは、他のプログラマーや内部サービスを含む(ただしこれらに限定されない)上記のサービスの実際のユーザーを指します。

20
Andres F.

すべてのサービスクラスにインターフェースがある理由の1つとして、洗練されたフレームワーク(Spring、JEEなど)とのやり取りがはるかに簡単になることが考えられます。これは、Javaがインターフェイスに対してインターセプターオブジェクトを単に生成できるためです(Java.lang.reflect.Proxy);具象クラスに対してそうすることははるかにトリッキーであり、複数の異なるクラスローダーを使用する場合、いくつかの非自明な警告で満たされます。これは、OSGiを使用する場合にも当てはまります。OSGiを使用すると、これらのサービスインターフェイスは、それらのサービスの実装とは異なるクラスローダーからロードされます。これにより(サービスディスカバリファブリックと合わせて)、各サービスとそのクライアントの間に非常に強力な分離が適用されます。クライアントは文字通りできない抽象化を壊します。

IDEが1回のクリックでクラスからインターフェイスを抽出できる場合でも、必ずしも正しいインターフェイスを抽出するわけではないことに注意してください。知性はまだ必要です。

12
Donal Fellows

インターフェースを最初に使用するとオーバーヘッドのように見えますが、後で機能を拡張する必要があるときに、これらのインターフェースが非常に役立ちます。

具体的な実装のみを行う場合は、コードの実装を大幅に変更する必要があります。ただし、インターフェースを使用する場合は、インターフェースの規約を実装する新しいクラスを1つ追加するだけです(また、古いインターフェースの参照変数から新しいクラスオブジェクトを呼び出すことができます)。 。

1
kundan bora