54

私は番号を持っているProcessorクラスは2つの非常に異なることをしますが、共通コードから呼び出されます(「制御の逆転」の状況)。

私は、あなたがすべてを継承すべきかどうかを決める際に、どのような設計上の考慮事項を私が認知するべきであるか(あるいは、あなたがUSSのために認識している)BaseProcessor、または実装するIProcessorインターフェイスとして。


7 답변


143

一般に、ルールは次のようになります。

  • 継承は、is-a関係。
  • インタフェースを実装すると、できる関係。

これをもっと具体的に言えば、例を見てみましょう。ザSystem.Drawing.Bitmapクラスis-anイメージ(そして、そのように、それはImageクラス)が、それもできる処分するので、IDisposableインタフェース。またできるシリアライゼーションは、ISerializableインタフェース。

しかしもっと実際的には、C#では複数の継承をシミュレートするためにインターフェースがよく使われます。もしあなたのProcessorクラスは次のようなものから継承する必要がありますSystem.ComponentModel.Component、あなたはほとんど選択肢がありませんが、IProcessorインタフェース。

実際には、インターフェースと抽象基本クラスの両方が、特定のクラスが何を行うことができるかを指定するコントラクトを提供します。この契約を宣言するためにインタフェースが必要であるという共通の神話ですが、それは正しくありません。私の心の最大の利点は、抽象基本クラスでサブクラスにデフォルトの機能を提供できることです。しかし、意味のあるデフォルトの機能がない場合、メソッド自体を次のようにマークすることはできません。abstractインタフェースを実装する場合と同様に、派生クラス自身で実装する必要があります。

このような質問に対する回答については、.NET Frameworkデザインガイドラインこれは、クラスとインタフェースの選択について言います。

一般に、クラスは抽象を公開するための好ましい構築物です。

インターフェイスの主な欠点は、APIの進化を許容するという点で、クラスよりもはるかに柔軟性がないということです。インターフェイスを出荷すると、メンバーのセットは永久に固定されます。インタフェースへの追加は、そのインタフェースを実装する既存の型を破壊します。

クラスはもっと柔軟性を提供します。すでに出荷されたクラスにメンバーを追加することができます。メソッドが抽象メソッドでない限り(つまり、メソッドのデフォルト実装を提供している限り)、既存の派生クラスは変更されずにそのまま機能します。

[。 。 。 ]

インタフェースを優先する最も一般的な主張の1つは、実装から契約を分離できることです。しかし、引数は、クラスを使用して実装から実装を分離できないと誤って想定しています。具体的な実装とは別のアセンブリにある抽象クラスは、そのような分離を実現するための素晴らしい方法です。

一般的な推奨事項は次のとおりです。

  • 行うインタフェースを介してクラスを定義することを推奨します。
  • 行うインタフェースの代わりに抽象クラスを使用して、実装を実装から切り離します。抽象クラスは、正しく定義されていれば、契約と実装の間で同じ程度のデカップリングを可能にします。
  • 行う値型の多態的な階層を提供する必要がある場合は、インタフェースを定義します。
  • 検討する複数の継承と同様の効果を得るためのインターフェースを定義します。

クリス・アンダーソンは、この最後の教義との特定の合意を表明し、

抽象型はバージョンをはるかに良くし、将来の拡張性を可能にしますが、基本型のみを焼き付けます。インタフェースは、時間の経過とともに不変である2つのオブジェクト間のコントラクトを実際に定義するときに適切です。抽象基本型は、型のファミリの共通基底を定義するのに適しています。


  • ありがとう - 素晴らしい答え...同上。ただ1つの注意点があります:もしあなたが公開する「パブリックタイプ」としての基底クラスと、そのクラスのユーザーに、そこから継承するための基本クラス...(将来的に)共通の動作を置くためのどこかにそれらを与えるためにアドバイスしてください....そしてリンクのおかげでヒープ。私はいくつかの良い読書をしています;-) - corlettk
  • インターフェイスはもっと必要なものであることに注意してください。 - awiebe
  • この回答は、Java 8で行われた最近の変更を考慮して、インターフェースにデフォルトのメソッドを導入することでより真実になります。そうでなければ、インタフェースを実装しているすべてのコードを破ることなく、新しいコントラクト/メソッドを導入する他の方法はありませんでした。 (抽象クラスとインタフェースの間の線はこの変更によりぼやけていきますが) - Harshdeep
  • このアドバイスは、'ヘッドファーストデザインパターン'の勢いとほぼ反対です。私はエキスパートではありませんが、マントラは継承以上の構成を好んでいました。誰もこれを解くことができますか? - alimack
  • @ali私は矛盾を一切見ない。確かに、私はこの答えを書いてから何年も経っていますが、もう一度見直すと、私は構成上の相続に賛成しているところを見ていません。それは言うことができません何でも実際には構図について。継承によってモデル化された強力な関係が必要ない場合は、合成がより良い選択です。必要以上に強い関係を表現してはいけません。 - Cody Gray

7

リチャード、

なぜそれらの間を選ぶのですか?私はIProcessorインターフェイスを持っています出版されたタイプ(システムの他の場所で使用するため)。そのようにしてIProcessorのさまざまなCURRENT実装が共通の振る舞いを持つ場合、抽象的なBaseProcessorクラスはその共通の振る舞いを実装するための本当の良い場所になります。

このようにして、将来、BaseProcessorのサービス用ではなかったIProcessorが必要な場合、それを持っていなくても(おそらくそれを隠していなくても)...しかし、それを望む人は、重複したコード/コンセプトで

ちょうど私の謙虚な意見。

乾杯。キース。


  • ありがとうKeith - それは良い点です。私は本当に迷っていました。どちらもうまくカバーしています。 - probably at the beach
  • うーん、私はこれに同意しない。あなたは一般的に、そのインタフェースの具体的な実装なしにインタフェースを公開するべきではありません。インターフェイスを公開する際のもう一つの問題はバージョン管理です(詳細は私の答えを見てください)。私はあなたが両方をする必要があるとは思わない。これは設計上の意思決定に常に最善の答えを示すわけではありません。両方をやろうとすると複雑さが増しますが、私はそれが実際にどのように優れているかを見るのに苦労しています。インターフェイスではなく抽象基本クラスを使用するだけで、あなたが欠けていることはありません。 - Cody Gray

5

インターフェースは「契約」です。これらは、クラス、つまりプロパティ、メソッド、イベントなどのメンバーを実装するクラスです。

基本クラス(具体的または抽象的ではない)は、あるエンティティの原型です。これは、実際の物理的または概念的なものに共通するものを表すエンティティです。

いつインターフェイスを使用するのですか?

いくつかのタイプは、少なくとも、消費者が気にするべきいくつかの振る舞いと性質を持っていることを宣言する必要があるときはいつでも、それを使って何らかの仕事を達成する。

ベースクラス(具体的および/または抽象的)を使用する場合は、

あるグループのエンティティが同じアーキタイプを共有する場合、BはAが相違しているためAを継承しますが、BはAと識別できます。


例:

テーブルについて話しましょう。

  • リサイクル可能なテーブルを受け入れる=>これは "IRecyclableTable"のようなインタフェースで定義しなければならず、すべてのリサイクル可能なテーブルに "リサイクル"メソッド

  • デスクトップテーブルが必要です=>これは継承で定義する必要があります。 「デスクトップテーブル」は「テーブル」です。すべてのテーブルには共通のプロパティと動作があり、デスクトップには共通のプロパティと動作があり、デスクトップテーブルが他のタイプのテーブルとは異なる動作をするようなものが追加されます。

私はアソシエーションについて話すことができます。つまり、オブジェクトグラフの両方の場合の意味ですが、私の大胆な意見では、概念的な観点から議論を行う必要がある場合、私はこの議論で正解します。


  • 「リサイクル可能なテーブル」とは何ですか。私はおそらくIRecyclableインターフェイスとは無関係オブジェクトはリサイクル可能である必要があります(つまり、表を表さない他のクラス)。そして、私はまだすべてのテーブルをTableクラスを含むDesktopTableそしてPicnicTableそしてFoldingTable。 - Cody Gray
  • 私のテキストを再確認すると、私はあなたと違う何かを言っているのですか? :)"リサイクル可能な表":"例"についてのことをどのように開始するかをお読みください。これは単なる例であり、オブジェクトグラフ、デザイン、または実際のケースから完全に分離されています。 - Matías Fidemraizer
  • とにかく、何か提案してくれてありがとう、私の言葉に間違ってはいけない、ねえ! :) - Matías Fidemraizer

1

私は設計上の選択肢があまりよくありませんが、尋ねられれば、拡張するメンバーだけがあればiProcessorインターフェースを実装する方が好きです。拡張する必要のない他の関数がある場合は、ベースプロセッサーから継承するほうがよいでしょう。


  • @jitendra - ありがとう、なぜこれがより良い選択肢であるかについてもう少し詳しく説明できますか? - probably at the beach
  • なぜあなたはインターフェイスを好むのですか?抽象基本クラスを上回るインターフェースの利点は何ですか?私はあなたの本能に完全に同意しません。常に複数の継承をシミュレートする必要がある場合を除いて、インタフェース上で抽象基本クラスを選択します。 - Cody Gray
  • いくつかの関数しかない場合は、基本クラスで使用可能なすべての関数を実装する必要がないので、継承は理にかなっています。基本クラスの関数とパラメーターの完全なリストについて十分な情報がある必要はありません。したがって、その場合、継承はより理にかなっています。 - jitendragarg
  • また、抽象クラスではなく通常の基本クラスを作成し、後でその基本クラスを使用することになっている場合でも、必要な関数のみを継承することができます。しかし、そのシナリオでは、インタフェースがクラスよりも軽いと思われるため、パフォーマンスが低下します。さらに、抽象クラスを介してインタフェースを使用する場合、他の開発者は、基本クラスからの2つの関数だけを使用するために、すべての関数を実装する必要さえありません。したがって、ベースクラスの再利用性も要因です。そのような選択を行う前に考慮する必要があります。 - jitendragarg
  • @cody gray私が間違っていなければ、抽象クラス全体を実装することになっています。これは再利用性の場合には悪い考えです。再び、それは私の見解です。間違っているかもしれない。間違っていれば、コメントをうまく編集してください。 - jitendragarg

1

デザインの純度をさておき、一度しか継承することはできません。フレームワーククラスを継承する必要がある場合は疑問があります。

あなたが選択肢がある場合、実際には最も入力を節約するオプションを選択することができます。とにかく純粋な選択です。

編集:

基本クラスがある実装を持つなら、これは有用かもしれません、純粋にabstactであれば、それはインタフェースかもしれません。


  • これは良い点です。多くの場合、基底クラスを選択すると最も多くの型を保存します。すべてのコア関数を呼び出す複数のオーバーロードを持つメソッドがある場合は、すべてのパラメータの検証、境界チェック、および関数オーバーロードを基本クラスに配置できます。これは、導かれたクラスが、コアの「肉とジャガイモ」を実装するだけでよいことを意味する。機能性。これはインターフェイスを使用するオプションではありません。そのインターフェイスを実装するクラスは毎回すべての作業を行う必要があります。 - Cody Gray
  • @Cody Grey最後の部分については、基本クラスはインターフェイスを実装し、インターフェイスの実装は仮想ですので、これは大文字と小文字が混在しています!ちょうど冗談、気にしないでください。 - Matías Fidemraizer

1

選択した項目が問題でない場合は、常に[インタフェース]を選択します。より柔軟に対応できます。将来の変更から保護するかもしれません(基本クラスで何かを変更する必要がある場合、継承されたクラスが影響を受ける可能性があります)。また、詳細をカプセル化することもできます。あなたがDependency Injectionの制御の反転を使用している場合、彼らはインターフェイスを好む傾向があります。

継承を避けることができない場合は、両方を一緒に使用することをお勧めします。インタフェースを実装する抽象基本クラスを作成します。あなたの場合、ProcessorBaseはIProcessorを実装します。

ASP.NET MVCのControllerBaseおよびIControllerに似ています。


  • 面白いのは、インターフェースがより柔軟であると主張し、将来の変更の場合にこれを正当化することです。将来、インターフェイスにいくつかの機能を追加する必要がある場合はどうなりますか?そのインタフェースを実装した各クラスはまた、追加したメソッドを実装する必要があります。基本クラスはデフォルト実装を提供するだけなので、基本クラスの場合はそうではありません。 - Cody Gray
  • '将来の変更'異なる話がある。あなたの場合、より良いやり方は抽象クラスを使用することですが、そうすることで、インタフェースクラスの抽象クラス(Iベースとベースクラスの両方)を好むかもしれません。また、異なる視点から、あなたがデザインしているクラスではなく、クラスを使用している場合は、それらの全体ではなく、2つまたは3つの方法を見る方がより好きです。この場合、インタフェースは懸念の分離に優れています。 1つのインターフェースを使用して別のことを行うことができます。これらのインターフェイスはすべて実際には1つのクラスになります。 - Hendry Ten
  • インターフェイスに機能を追加する方法について:通常、新しいタイプのインターフェイスをインターフェイスに追加することはできません(契約タイプやリポジトリの場合ではなく)。新しいインターフェイスを作成することもできます(別のインターフェイスからC#でインターフェイスを派生させることもできます)懸念事項がうまく実行されます。 - Hendry Ten
  • 私はこの種の議論が好きではありません。リファクタリングの理由からデザインを行うべきではありません。優れた設計はリファクタリングが容易であり、インタフェース使用の継承に関するものではありません。ソフトウェアは変更されていますが、これは残業の解決にどのような種類の要件が適用されるかに注意を払って行うことができるため、継承を考慮しても間違いがなければ、良い継承ツリーは論理を追加、変更、その意味はそのままであるべきである。 - Matías Fidemraizer

-1

SOLIDの原則がプロジェクトの保守性と拡張性を向上させることを考えれば、継承よりもインターフェイスを優先したいと思います。

また、インターフェイスに「追加機能」を追加する必要がある場合は、インターフェイス・セグレゲーションの原則であるSOLIDのIに従って、新しいインターフェイスを作成することをお勧めします。

リンクされた質問


最近の質問