インターフェースはめったに使用せず、他のコードでは一般的です。
また、コード内でめったにサブクラスとスーパークラスを作成しません(独自のクラスを作成します)。
- それは悪いことですか?
- このスタイルを変更することをお勧めしますか?
- このスタイルには副作用がありますか?
- これは、私が大規模なプロジェクトに取り組んだことがないためですか?
インターフェースはめったに使用せず、他のコードでは一般的です。
また、コード内でめったにサブクラスとスーパークラスを作成しません(独自のクラスを作成します)。
回答:
インターフェイスを使用する理由はいくつかあります。
http://msdn.microsoft.com/en-us/library/3b5b8ezk(v=vs.80).aspx
インターフェイスは、プログラミングの他の要素と同じです。必要ない場合は、使用しないでください。スタイルの問題としてそれらが広範囲に使用されているのを見てきましたが、インターフェイスが提供する特別なプロパティと機能が必要ない場合、「理由だけで」それらを使用する利点はありません。
クラスの継承とインターフェースの両方にそれぞれの場所があります。継承は「である」ことを意味し、インターフェースは「動作」を定義するコントラクトを提供します。
インターフェースをより頻繁に使用することは、悪い習慣ではないということです。現在、ビルワーグナーの「効果的なC#-C#を改善する50の具体的な方法」を読んでいます。項目番号22には、「継承に対するインターフェイスの定義と実装を優先する」という引用があります。
一般に、概念的に関連するタイプ間の共通の動作の特定の実装を定義する必要がある場合、基本クラスを使用します。より頻繁にインターフェイスを使用します。実際、私は通常、クラスの作成を開始するときにクラスのインターフェースを定義することから始めます...インターフェースをコンパイルしなくても、公開API始めからクラス。インターフェイスを実装するクラスが複数あり、実装ロジックが同一であることがわかった場合にのみ、型間で共通の基本クラスを実装することが理にかなっているかどうかを自問します。
ビル・ワグナーズの本からの引用のカップル...
すべての派生クラスには、その動作がすぐに組み込まれます。インターフェイスにメンバーを追加すると、そのインターフェイスを実装するすべてのクラスが壊れます。新しいメソッドは含まれず、コンパイルもされません。各実装者は、その型を更新して新しいメンバーを含める必要があります。抽象基本クラスとインターフェースのどちらを選択するかは、長期にわたって抽象化をサポートする最善の方法の問題です。インターフェースが修正されました:任意のタイプが実装できる一連の機能のコントラクトとしてインターフェースをリリースします。基本クラスは時間をかけて拡張できます。これらの拡張機能は、すべての派生クラスの一部になります。2つのモデルを組み合わせて、複数のインターフェイスをサポートしながら実装コードを再利用できます。」新しいメソッドは含まれず、コンパイルもされません。各実装者は、その型を更新して新しいメンバーを含める必要があります。抽象基本クラスとインターフェースのどちらを選択するかは、長期にわたって抽象化をサポートする最善の方法の問題です。インターフェースが修正されました:任意のタイプが実装できる一連の機能のコントラクトとしてインターフェースをリリースします。基本クラスは時間をかけて拡張できます。これらの拡張機能は、すべての派生クラスの一部になります。2つのモデルを組み合わせて、複数のインターフェイスをサポートしながら実装コードを再利用できます。」新しいメソッドは含まれず、コンパイルもされません。各実装者は、その型を更新して新しいメンバーを含める必要があります。抽象基本クラスとインターフェースのどちらを選択するかは、長期にわたって抽象化をサポートする最善の方法の問題です。インターフェースが修正されました:任意のタイプが実装できる一連の機能のコントラクトとしてインターフェースをリリースします。基本クラスは時間をかけて拡張できます。これらの拡張機能は、すべての派生クラスの一部になります。2つのモデルを組み合わせて、複数のインターフェイスをサポートしながら実装コードを再利用できます。」任意のタイプが実装できる一連の機能のコントラクトとしてインターフェースをリリースします。基本クラスは時間をかけて拡張できます。これらの拡張機能は、すべての派生クラスの一部になります。2つのモデルを組み合わせて、複数のインターフェイスをサポートしながら実装コードを再利用できます。」任意のタイプが実装できる一連の機能のコントラクトとしてインターフェースをリリースします。基本クラスは時間をかけて拡張できます。これらの拡張機能は、すべての派生クラスの一部になります。2つのモデルを組み合わせて、複数のインターフェイスをサポートしながら実装コードを再利用できます。」
「コーディングインターフェイスは、基本クラスタイプへのコーディングよりも柔軟性が他の開発者に提供されます。」
「インターフェイスを使用してクラスのAPIを定義すると、柔軟性が高まります。」
「型がプロパティをクラス型として公開すると、そのクラスへのインターフェイス全体が公開されます。インターフェイスを使用すると、クライアントが使用するメソッドとプロパティのみを公開することを選択できます。」
「基本クラスは、関連する具象型に共通の動作を記述および実装します。インターフェースは、無関係な具象型が実装できる機能のアトミックな部分を記述します。クラスは、作成する型を定義します。違いを理解すれば、変化に対してより弾力性のある表現力豊かなデザインを作成できます。クラス階層を使用して関連タイプを定義します。これらのタイプに実装されたインターフェースを使用して機能を公開します。
言及されていないことの1つはテストです。すべてのC#モックライブラリとJavaのモックライブラリでは、インターフェイスを実装しない限り、クラスをモックできません。これにより、アジャイル/ TDDプラクティスに従って多くのプロジェクトが導かれ、すべてのクラスに独自のインターフェイスが提供されます。
「カップリングを減らす」ため、このベストプラクティスを考える人もいますが、私は同意しません。これは、言語の欠陥に対する単なる回避策だと思います。
インターフェイスは、抽象的には「同じこと」を行うが異なる方法で行う2つ以上のクラスがある場合に最もよく使用されると思います。
たとえば、.Netフレームワークには、もののリストを格納する複数のクラスがありますが、それらはすべて異なる方法でそれらのものを格納します。したがって、IList<T>
さまざまなメソッドを使用して実装できる抽象インターフェイスを使用することは理にかなっています。
2+クラスを交換可能にするか、将来交換可能にする場合にも使用する必要があります。将来、リストを保存する新しい方法が登場した場合、コード全体でAwesomeList<T>
使用IList<T>
していると仮定すると、使用AwesomeList<T>
するように変更すると、数百/千行ではなく、数十行しか変更されなくなります。
適切なときに継承とインターフェイスを使用しない主な結果は、密結合です。これを認識することを学ぶのは少し難しいかもしれませんが、通常、最も明白な症状は、変更を行うときに、波及効果の変更を行うために他の多くのファイルを頻繁に調べなければならないことです。
いいえ、これはまったく悪くありません。共通のインターフェースを持つ2つのクラスを実行時に交換する必要がある場合に限り、明示的なインターフェースを使用する必要があります。交換可能にする必要がない場合は、継承させないでください。それはそれと同じくらい簡単です。継承は脆弱であり、可能な限り回避する必要があります。回避できる場合、またはジェネリックを代わりに使用する場合は、実行してください。
問題は、C#やJavaのようなコンパイル時のジェネリックが弱い言語では、すべてのクラスが同じベースから継承しない限り、複数のクラスを処理できる1つのメソッドを記述する方法がないため、DRYに違反することになります。ただし、C#4 はこれに対処dynamic
できます。
継承はグローバル変数のようなものです-一度追加してコードがそれに依存するようになると、神はそれを取り除くのを手伝います。ただし、いつでも追加できます。また、並べ替えのラッパーを使用して、基本クラスを変更せずに追加することもできます。
はい、おそらくインターフェースを使用しないことは(または非常にまれです)悪いことです。インターフェイス(抽象的な意味で、C#/ Java言語構成はその抽象的な意味のかなり良い近似です)は、システムとサブシステム間の明示的な相互作用点を定義します。これにより、カップリングが減少し、システムのメンテナンス性が向上します。保守性を向上させるものと同様に、システムが大きくなるほど重要になります。
状況は変化しています(MS Moles)が、インターフェイスのみにコーディングするのが良いと思う主な理由は、IoCアーキテクチャに簡単にモックし、自然に適合することです。
IMOは、インターフェイス、または可能な限り完全にダムDAOでのみ作業する必要があります。この考え方に着手し、インターフェイスを介してそれ自体を公開せず、正直に言って具体的なオブジェクトを介してすべてを実行するライブラリを使用し始めると、それはすべて少し不格好に感じます。
昔ながらのプロジェクトでは、循環参照を回避するためにインターフェイスを使用します。循環参照は、長期的には大きなメンテナンス問題になる可能性があるためです。
悪い:
class B; // forward declaration
class A
{
B* b;
};
class B
{
A* a;
}
悪くない:
class PartOfClassB_AccessedByA
{
};
class A
{
PartOfClassB_AccessedByA* b;
};
class B : public PartOfClassB_AccessedByA
{
A* a;
}
通常、A、B、PartOfClassB_AccessedByAは個別のファイルで実装されます。