特定のガイドラインでは、継承が明確ではないクラス(IDomesticated
)のコントラクトを定義する場合はインターフェイスを使用し、クラスが別の拡張である場合は継承(Cat : Mammal
、 Snake : Reptile
)、(私の意見では)これらのガイドラインが灰色の領域に入る場合があります。
たとえば、私の実装がCat : Pet
だったとします。 Pet
は抽象クラスです。それをCat : Mammal, IDomesticated
に展開する必要があります。ここで、Mammal
は抽象クラスで、IDomesticated
はインターフェースです。または、私は [〜#〜] kiss [〜#〜] / [〜#〜] yagni [〜#〜] の原則と矛盾していますか? m Wolf
から継承できないPet
クラスが将来存在するかどうか不明ですか?
比喩的なCat
sとPet
sから離れて、着信データのソースを表すクラスがあるとします。彼らは何とかして同じベースを実装する必要があります。抽象Source
クラスにいくつかの汎用コードを実装し、それから継承することができます。また、ISource
インターフェイスを作成し(これにより「より適切」に感じる)、各クラスの汎用コードを再実装することもできます(直感的ではありません)。最後に、抽象クラスとインターフェースの両方を作成することで、「ケーキを手に入れて食べる」ことができました。何が一番いい?
これら2つのケースは、抽象クラスのみ、インターフェースのみを使用し、抽象クラスとインターフェースの両方を使用することのポイントをもたらします。これらはすべて有効な選択肢ですか、それとも、あるルールを別のルールに対して使用する必要がある場合の「ルール」はありますか?
基本的に同じものを表す場合(Source
とISource
は同じメンバーを持っている場合)を含む「抽象クラスとインターフェースの両方を使用すること」によって、それを明確にしたいと思います。しかし、インターフェースがコントラクトを指定する間、クラスは一般的な機能を追加します。
また、注目に値するのは、この質問は主に多重継承をサポートしていない言語(.NETやJavaなど)に関するものであることです。
最初の経験則として、私はインターフェイスよりも抽象クラスを好む 。NET設計ガイドラインに基づく 。推論は.NETよりもはるかに広く適用されますが、本 Framework Design Guidelines でより詳しく説明されています。
抽象基本クラスの設定の背後にある主な理由はバージョン管理です。これは、既存のクライアントを壊すことなく、常に新しい仮想メンバーを抽象基本クラスに追加できるためです。それはインターフェースでは不可能です。
インターフェースが依然として正しい選択であるシナリオがあります(特に、バージョン管理を気にしない場合)が、長所と短所を認識していると、正しい決定を行うことができます。
先に進む前の部分的な回答として、インターフェイスと基本クラスの両方を持つことは、そもそもインターフェイスに対してコーディングすることにした場合にのみ意味があります。インターフェースを許可する場合は、そのインターフェースに対してのみコーディングする必要があります。そうしないと、リスコフの代替原則に違反することになります。つまり、インターフェイスを実装する基本クラスを提供しても、コードにその基本クラスを使用させることはできません。
基本クラスに対してコード化することにした場合、インターフェースを持つことは意味がありません。
インターフェイスに対してコーディングする場合は、デフォルトの機能を提供する基本クラスを使用するかどうかはオプションです。必須ではありませんが、実装者の速度を上げる可能性があるため、サービスとして提供できます。
頭に浮かぶ例はASP.NET MVCです。リクエストパイプラインはIControllerで機能しますが、通常、動作の実装に使用するController基本クラスがあります。
最終的な答え:抽象基本クラスを使用する場合は、それのみを使用してください。インターフェースを使用する場合、基本クラスは実装者にとってオプションの礼儀です。
更新:Iもはやインターフェースよりも抽象クラスを好む、そして私は長い間使っていません;代わりに、SOLIDをガイドラインとして使用して、継承よりも構成を優先します。
(上記のテキストを直接編集することもできますが、投稿の性質を根本的に変えることになります。また、賛成票を投じるだけの価値があると考える人もいるので、元のテキストをそのままにして、代わりにこれを追加します注:投稿の後半部分はまだ意味があるため、削除するのも残念です。)
オブジェクトのcapabilitiesを記述するためにインターフェイスを使用する一方で、何かisを記述するために基本クラス(抽象かそうでないか)を使用する傾向があります。
猫is a哺乳類ですが、その1つであるcapabilitiesは、Pettableです。
または、別の言い方をすると、クラスは名詞であり、インターフェースは形容詞に近くマッピングされます。
MSDNから 抽象クラスとインターフェイスの推奨事項
コンポーネントの複数のバージョンを作成する予定がある場合は、抽象クラスを作成します。抽象クラスは、コンポーネントをバージョン管理するためのシンプルで簡単な方法を提供します。基本クラスを更新することにより、すべての継承クラスが変更に応じて自動的に更新されます。一方、インターフェースは一度作成すると変更できません。新しいバージョンのインターフェースが必要な場合は、まったく新しいインターフェースを作成する必要があります。
作成している機能がさまざまな異種オブジェクトにわたって役立つ場合は、インターフェースを使用します。抽象クラスは主に密接に関連するオブジェクトに使用する必要がありますが、インターフェースは関連のないクラスに共通の機能を提供するのに最適です。
小さく簡潔な機能を設計する場合は、インターフェースを使用してください。大規模な機能ユニットを設計する場合は、抽象クラスを使用します。
コンポーネントのすべての実装に共通の実装機能を提供する場合は、抽象クラスを使用します。抽象クラスではクラスを部分的に実装できますが、インターフェースにはメンバーの実装が含まれていません。
[〜#〜] dry [〜#〜] 原則と呼ばれるものもあります-自分を繰り返さないでください。
データソースの例では、異なる実装間で共通の一般的なコードがいくつかあると言います。私には、それを処理する最良の方法は、一般的なコードを含む抽象クラスと、それを拡張するいくつかの具象クラスを持つことです。
利点は、汎用コードのすべてのバグ修正がすべての具体的な実装に利益をもたらすことです。
インターフェースのみを使用する場合は、同じコードのいくつかのコピーを維持する必要があります。
抽象+インターフェースに関しては、それに対する直接の正当化がなければ、私はそれをしません。抽象クラスからインターフェイスを抽出するのは簡単なリファクタリングなので、実際に必要な場合にのみ実行します。
私は常に次のガイドラインを使用します。
主要な懸念のルールでは、クラスには常に主要な懸念があり、他には0以上の懸念があることを示しています( http://citeseer.ist.psu.edu/tarr99degrees.html を参照)。クラスが実装する必要のあるすべての型(独自のものと実装するすべてのインターフェース)を実装するため、インターフェースを介して実装するその他の0個以上。
複数の実装継承の世界(C++/Eiffelなど)では、インターフェースを実装するクラスから継承します。 (理論的には、実際にはうまく機能しない可能性があります。)
実装を完全に置き換えるオプションを提供する場合は、インターフェースを使用します。これは特に主要コンポーネント間の相互作用に適用され、これらは常にインターフェースによって切り離されるべきです。
単体テストでモックを有効にするなど、インターフェースを選択する技術的な理由もあります。
コンポーネントの内部では、抽象クラスを直接使用してクラスの階層にアクセスするだけで十分な場合があります。
インターフェースを使用し、実装クラスの階層がある場合、実装の共通部分を含む抽象クラスを作成することをお勧めします。例えば。
interface Foo
abstract class FooBase implements Foo
class FunnyFoo extends FooBase
class SeriousFoo extends FooBase
さらに複雑な階層のために、互いに継承する抽象クラスを増やすこともできます。
一般的なガイドラインについては、以下のSEの質問を参照してください。
インターフェースの実際の使用例:
Strategy_pattern の実装:戦略をインターフェースとして定義します。実行時に戦略の具体的な実装の1つを使用して、実装を動的に切り替えます。
関連のない複数のクラス間でcapabilityを定義します。
抽象クラスの実際の使用例:
Template_method_pattern の実装:アルゴリズムのスケルトンを定義します。子クラスはアルゴリズムの構造を変更できませんが、実装の一部を子クラスで再定義できます。
"has a"関係を持つ複数の関連クラス間で非静的変数と非最終変数を共有する場合。
abstradtクラスとインターフェイスの両方の使用:
抽象クラスを作成する場合は、抽象メソッドをインターフェイスに移動し、抽象クラスでそのインターフェイスを実装できます。抽象クラスのすべての使用例は、このカテゴリに分類できます。