30

抽象クラスよりもインターフェース+拡張メソッド(ミックスイン)の方が望ましいですか?

あなたの答えが「それは依存する」であるならば、それは何に依存しますか?

インターフェイスと拡張機能のアプローチには、2つの利点があります。

  • インターフェイスは複数回継承可能ですが、クラスは継承できません。
  • 拡張メソッドを使用して、インターフェイスを壊さない方法で拡張できます。(インターフェイスを実装するクライアントは、新しい基本実装を取得しますが、それでもオーバーライドできます。)

このアプローチの欠点についてはまだ考えていません。インターフェイスと拡張機能のアプローチが失敗するという、明らかに単純な理由があるかもしれません。

このトピックに関する2つの役立つ記事は次のとおりです。

4

3 に答える 3

25

拡張メソッドの欠点:C#3/VB9より前​​のクライアントはそれを簡単に使用できなくなります。

私に関する限り、これで終わりです。インターフェイスベースのアプローチの方がはるかに優れていると思います。その後、依存関係をうまくモックアウトすることができ、基本的にすべてがあまり緊密に結合されていません。専門化に関するものでない限り、私はクラス継承の大ファンではありません:)

編集:私はちょうど関連するかもしれないもう一つの利点を考えました。いくつかの具体的な実装は、いくつかの一般的な方法のより最適化されたバージョンを提供する可能性があります。

Enumerable.Countこれはこの良い例です。シーケンスが実装されているかどうかを明示的にチェックします。実装されている場合は、シーケンス全体を繰り返す代わりにリストをIList呼び出すことができるためです。仮想メソッドを持つ抽象クラスであったCount場合、明示的に認識している単一の実装が存在するのではなく、オーバーライドされた可能性があります。私はこれが常に関連していると言っているわけではなく、抽象クラスであるべきではありませんでした(絶対にそうではありません!)-小さな可能性のある欠点として指摘しているだけです。ここで、既存の動作を特殊化することにより、ポリモーフィズムが実際に適切になります(確かに、結果ではなくパフォーマンスにのみ影響する方法で)。IEnumerable<T>Count()List<T>IListIEnumerable<T>

于 2009-04-23T20:09:17.323 に答える
23

私見、これは間違った質問です。

あなたはそれが設計されているもののためにすべてを使うべきです。

  • 拡張メソッドはメンバーではありません。構文的にはメンバーのように見えるので、簡単に見つけることができます。
  • 拡張メソッドは、パブリック(または内部)インターフェースのみを使用できます。他の多くのクラスでも同じことができます。したがって、拡張メソッドは、オブジェクト指向の方法で実際にカプセル化されたものではありません。
  • これらは静的メソッドであり、単体テストでオーバーライドしたり、モックしたりすることはできません。Isは非OO言語機能であり、呼び出し元は静的にバインドされています。

  • 抽象基本クラスは、(実際の継承ではなく)「コードを再利用する」ために実際に誤用されることがよくあります。これは一般的に継承に適用されます。

質問は次のようになります。「インターフェイス、拡張メソッド、または基本クラスをいつ使用する必要がありますか?」

  • 契約が必要なときはいつでもインターフェースを使用してください(これは常に発生します)。
  • 本当の相続が必要な場合は、(抽象)基本クラスを使用します(それを判断する方法についての本を書くことができるので、そのままにしておきます)。インターフェイスもほとんど同時に実装されます。
  • 実装するのは型の責任ではないため、実際には型のメンバーであってはならないロジックの拡張メソッドを使用します。ただし、見つけやすくしたいので、メンバーのように呼び出すのが自然です。

編集:

または、「基本クラスに属さない再利用可能な機能を作成するにはどうすればよいですか?」という質問が必要です。

  • 機能を公開するインターフェースを作成する
  • 機能を実装する再利用可能なライブラリクラスを作成する
  • インターフェイスを実装し、再利用可能なクラスを集約して機能を再利用するクラスを作成します。

一般に、拡張メソッドは、特別な場合や特別な設計上の決定を除いて、ビジネスロジックにとって間違った場所です。

基本クラスは、まれなケースでのみ正しい決定です。疑わしいですが、そうではありません。疑いの余地なく、あなたはそれについてもう一度考えるべきです。

于 2009-04-23T22:24:35.410 に答える
1

インターフェイスはコードを少しすっきりさせる傾向があります。テストが少し簡単だと思います。拡張機能を追加すると、テスト可能なコードをクリーンに保ちながら、さらに柔軟性を追加できます。

私にとって、抽象クラスは常に不格好に見えました。インターフェイスを使用して、達成しようとしていることに固有のオブジェクトを返すオブジェクトファクトリを作成できます(関心の分離)。

何かを作るだけで、加算、減算、除算、乗算を行うMathというインターフェイスがあり、整数数学用に最適化されたMathを実装するIntMAthというクラスがあり、最適化されたMathを実装するFloatMathという別のクラスがあります。 Floating Mathの場合、他のすべてを処理するMathを実装するgeneralMathがあります。

フロートを追加する必要がある場合は、ファクトリMathFactory.getMath(typeof(float))を呼び出すことができ、渡すタイプがフロートの場合、FloatMathクラスを返すことを知るロジックがあります。

このようにして、すべてのクラスがより小さく、より保守しやすくなり、クラスを呼び出すコードがより小さくなります。

于 2009-04-23T21:19:19.683 に答える