インターフェイスを使用しないのは悪い習慣ですか?[閉まっている]


40

インターフェースはめったに使用せず、他のコードでは一般的です。

また、コード内でめったにサブクラスとスーパークラスを作成しません(独自のクラスを作成します)。

  • それは悪いことですか?
  • このスタイルを変更することをお勧めしますか?
  • このスタイルには副作用がありますか?
  • これは、私が大規模なプロジェクトに取り組んだことがないためですか?

10
これはどの言語ですか?

2
どの言語に加えて、これはどのようなパラダイムですか?
アルマンド

1
「インターフェース」の概念は言語を超えませんか?Javaには組み込みのInterface型がありますが、C ++開発者は多くの場合(Iで始まる)インターフェースも作成し、すべて同じ目的を果たします。
リケット

4
@リケット:いいえ、そうでもありません。問題は、C ++が複数のクラス継承を提供することです。C ++では、「インターフェイス」ははるかに概念的であり、実際には、それらが抽象基本クラスとして定義するものを主に使用します。#
DeadMG

2
@Ricketいいえ、特に手続き型言語やOOP言語ではなく関数型言語で作業している場合。
代替案

回答:


36

インターフェイスを使用する理由はいくつかあります。

  1. インターフェイスは、アプリケーションが特定の機能を提供するために、多くの場合関連のないオブジェクトタイプを必要とする状況に適しています。
  2. インターフェースは、複数のインターフェースを実装できる単一の実装を定義できるため、基本クラスよりも柔軟性があります。
  3. インターフェイスは、基本クラスから実装を継承する必要がない場合に適しています。
  4. インターフェイスは、クラス継承を使用できない場合に役立ちます。たとえば、構造体はクラスから継承できませんが、インターフェイスを実装できます。

http://msdn.microsoft.com/en-us/library/3b5b8ezk(v=vs.80).aspx

インターフェイスは、プログラミングの他の要素と同じです。必要ない場合は、使用しないでください。スタイルの問題としてそれらが広範囲に使用されているのを見てきましたが、インターフェイスが提供する特別なプロパティと機能が必要ない場合、「理由だけで」それらを使用する利点はありません。


13
彼の言語は指定されておらず、あなたの言語はあまりにも具体的です。たとえば、C ++では、構造は実際にクラスから継承でき、非抽象ベースを多重に継承できます。
-DeadMG

2
この答えはC#のようですが、#4を抜くとJavaにも関係します。C++ではもちろん多重継承が許可されているため、C ++にのみ適用されます。
リケット

2
また、私は最後の声明に同意しません。プログラマーがすべきであるが、彼らはそれらを必要としないと感じるのでそうすべきでない多くの良い慣行があると思います。質問者は周りを見回し、他の全員がこのことをしているのを見て、多分彼はそれをやるべきではないかと考えながら、最初に「なぜ」と尋ねるのが非常に賢明だと思います。
リケット

3
@Ricket:理由は、私の答えの4つの箇条書きにあります。これらの理由のいずれも当てはまらない場合、インターフェイスは必要ありません。
ロバートハーベイ

17

クラスの継承とインターフェースの両方にそれぞれの場所があります。継承は「である」ことを意味し、インターフェースは「動作」を定義するコントラクトを提供します。

インターフェースをより頻繁に使用することは、悪い習慣ではないということです。現在、ビルワーグナーの「効果的なC#-C#を改善する50の具体的な方法」を読んでいます。項目番号22には、「継承に対するインターフェイスの定義と実装を優先する」という引用があります。

一般に、概念的に関連するタイプ間の共通の動作の特定の実装を定義する必要がある場合、基本クラスを使用します。より頻繁にインターフェイスを使用します。実際、私は通常、クラスの作成を開始するときにクラスのインターフェースを定義することから始めます...インターフェースをコンパイルしなくても、公開API始めからクラス。インターフェイスを実装するクラスが複数あり、実装ロジックが同一であることがわかった場合にのみ、型間で共通の基本クラスを実装することが理にかなっているかどうかを自問します。

ビル・ワグナーズの本からの引用のカップル...

すべての派生クラスには、その動作がすぐに組み込まれます。インターフェイスにメンバーを追加すると、そのインターフェイスを実装するすべてのクラスが壊れます。新しいメソッドは含まれず、コンパイルもされません。各実装者は、その型を更新して新しいメンバーを含める必要があります。抽象基本クラスとインターフェースのどちらを選択するかは、長期にわたって抽象化をサポートする最善の方法の問題です。インターフェースが修正されました:任意のタイプが実装できる一連の機能のコントラクトとしてインターフェースをリリースします。基本クラスは時間をかけて拡張できます。これらの拡張機能は、すべての派生クラスの一部になります。2つのモデルを組み合わせて、複数のインターフェイスをサポートしながら実装コードを再利用できます。」新しいメソッドは含まれず、コンパイルもされません。各実装者は、その型を更新して新しいメンバーを含める必要があります。抽象基本クラスとインターフェースのどちらを選択するかは、長期にわたって抽象化をサポートする最善の方法の問題です。インターフェースが修正されました:任意のタイプが実装できる一連の機能のコントラクトとしてインターフェースをリリースします。基本クラスは時間をかけて拡張できます。これらの拡張機能は、すべての派生クラスの一部になります。2つのモデルを組み合わせて、複数のインターフェイスをサポートしながら実装コードを再利用できます。」新しいメソッドは含まれず、コンパイルもされません。各実装者は、その型を更新して新しいメンバーを含める必要があります。抽象基本クラスとインターフェースのどちらを選択するかは、長期にわたって抽象化をサポートする最善の方法の問題です。インターフェースが修正されました:任意のタイプが実装できる一連の機能のコントラクトとしてインターフェースをリリースします。基本クラスは時間をかけて拡張できます。これらの拡張機能は、すべての派生クラスの一部になります。2つのモデルを組み合わせて、複数のインターフェイスをサポートしながら実装コードを再利用できます。」任意のタイプが実装できる一連の機能のコントラクトとしてインターフェースをリリースします。基本クラスは時間をかけて拡張できます。これらの拡張機能は、すべての派生クラスの一部になります。2つのモデルを組み合わせて、複数のインターフェイスをサポートしながら実装コードを再利用できます。」任意のタイプが実装できる一連の機能のコントラクトとしてインターフェースをリリースします。基本クラスは時間をかけて拡張できます。これらの拡張機能は、すべての派生クラスの一部になります。2つのモデルを組み合わせて、複数のインターフェイスをサポートしながら実装コードを再利用できます。」

「コーディングインターフェイスは、基本クラスタイプへのコーディングよりも柔軟性が他の開発者に提供されます。」

「インターフェイスを使用してクラスのAPIを定義すると、柔軟性が高まります。」

「型がプロパティをクラス型として公開すると、そのクラスへのインターフェイス全体が公開されます。インターフェイスを使用すると、クライアントが使用するメソッドとプロパティのみを公開することを選択できます。」

「基本クラスは、関連する具象型に共通の動作を記述および実装します。インターフェースは、無関係な具象型が実装できる機能のアトミックな部分を記述します。クラスは、作成する型を定義します。違いを理解すれば、変化に対してより弾力性のある表現力豊かなデザインを作成できます。クラス階層を使用して関連タイプを定義します。これらのタイプに実装されたインターフェースを使用して機能を公開します。


2
まあ、私はそれを読んで、それが良い答えだと思った。
エリックキング

1
@ mc10何が問題なのかわかりません。彼は本を参照し、彼の答えの下半分はその本からの引用であり、時間を無駄にしたくない場合に彼に明らかに知らせてくれます。
ピート

@Pete答えが悪いとは言わなかったが、長すぎるとだけ言った。引用符全体が必要な場合があるとは思いません。
ケビンジ

3
ハハ、これがtl; drの場合、このStackExchange-high-quality-answers全体が好きになるかどうかわかりません。
ニック

ハハ、みんなありがとう。はい、答えは少し長くかかったと思いましたが、インターフェイスと継承の長所と短所、およびシナリオそれぞれがより適切です。おそらく、本を直接引用しても、私の答えに大きな価値はありませんでした。
ジョンコネリー

8

言及されていないことの1つはテストです。すべてのC#モックライブラリとJavaのモックライブラリでは、インターフェイスを実装しない限り、クラスモックできません。これにより、アジャイル/ TDDプラクティスに従って多くのプロジェクトが導かれ、すべてのクラスに独自のインターフェイスが提供されます。

「カップリングを減らす」ため、このベストプラクティスを考える人もいますが、私は同意しません。これは、言語の欠陥に対する単なる回避策だと思います。


インターフェイスは、抽象的には「同じこと」を行うが異なる方法で行う2つ以上のクラスがある場合に最もよく使用されると思います。

たとえば、.Netフレームワークには、もののリストを格納する複数のクラスがありますが、それらはすべて異なる方法でそれらのものを格納します。したがって、IList<T>さまざまなメソッドを使用して実装できる抽象インターフェイスを使用することは理にかなっています。

2+クラスを交換可能にするか、将来交換可能にする場合にも使用する必要があります。将来、リストを保存する新しい方法が登場した場合、コード全体でAwesomeList<T>使用IList<T>していると仮定すると、使用AwesomeList<T>するように変更すると、数百/千行ではなく、数十行しか変更されなくなります。


5

適切なときに継承とインターフェイスを使用しない主な結果は、密結合です。これを認識することを学ぶのは少し難しいかもしれませんが、通常、最も明白な症状は、変更を行うときに、波及効果の変更を行うために他の多くのファイルを頻繁に調べなければならないことです。


4

いいえ、これはまったく悪くありません。共通のインターフェースを持つ2つのクラスを実行時に交換する必要がある場合に限り、明示的なインターフェースを使用する必要があります。交換可能にする必要がない場合は、継承させないでください。それはそれと同じくらい簡単です。継承は脆弱であり、可能な限り回避する必要があります。回避できる場合、またはジェネリックを代わりに使用する場合は、実行してください。

問題は、C#やJavaのようなコンパイル時のジェネリックが弱い言語では、すべてのクラスが同じベースから継承しない限り、複数のクラスを処理できる1つのメソッドを記述する方法がないため、DRYに違反することになります。ただし、C#4 はこれに対処dynamic できます。

継承はグローバル変数のようなものです-一度追加してコードがそれに依存するようになると、神はそれを取り除くのを手伝います。ただし、いつでも追加できます。また、並べ替えのラッパーを使用して、基本クラスを変更せずに追加することもできます。


1
ダウン投票の理由は?
DeadMG

わからない、賛成票を持っている。いい答えでした。
ブライアンベッチャー

3

はい、おそらくインターフェースを使用しないことは(または非常にまれです)悪いことです。インターフェイス(抽象的な意味で、C#/ Java言語構成はその抽象的な意味のかなり良い近似です)は、システムとサブシステム間の明示的な相互作用点を定義します。これにより、カップリングが減少し、システムのメンテナンス性が向上します。保守性を向上させるものと同様に、システムが大きくなるほど重要になります。


3

私は何年もインターフェースを使用していません。もちろん、これは、私が何年もの間、ほぼ排他的にErlangでプログラミングしており、インターフェースの概念全体が存在しないためです。(あなたが得る最も近い「行動」であり、あなたは本当にハードに目を細めていない限り、それは実際には同じことではない、あなたの目の隅のそれらを見て。)

したがって、実際には、あなたの質問はパラダイム依存(この場合はOOP)であり、さらに、まったく言語依存です(インターフェイスのないOOP言語があります)。


2

Javaの使用について話している場合、インターフェイスを使用する理由の1つは、コード生成ライブラリなしでプロキシオブジェクトを使用できるようにすることです。これは、Springのような複雑なフレームワークを使用している場合に大きな利点となります。さらに、一部の機能にインターフェイスが必要です。RMIはこの典型的な例です。インターフェイスを(継承するjava.rmi.Remote)提供する機能を記述する必要がありますが、実装する場合はそうです。


1

状況は変化しています(MS Moles)が、インターフェイスのみにコーディングするのが良いと思う主な理由は、IoCアーキテクチャに簡単にモックし、自然に適合することです。

IMOは、インターフェイス、または可能な限り完全にダムDAOでのみ作業する必要があります。この考え方に着手し、インターフェイスを介してそれ自体を公開せず、正直に言って具体的なオブジェクトを介してすべてを実行するライブラリを使用し始めると、それはすべて少し不格好に感じます。


0

昔ながらのプロジェクトでは、循環参照を回避するためにインターフェイスを使用します。循環参照は、長期的には大きなメンテナンス問題になる可能性があるためです。

悪い:

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は個別のファイルで実装されます。


0

インターフェイスベースのプログラミングは、コードの柔軟性とテストの容易化に役立ちます。実装クラスは、クライアントコードに触れることなく変更できます[柔軟性]。コードをテストする間、実際のoInterfaceベースのプログラミングを置き換えることができますが、コードをより柔軟で簡単にテストすることができます。実装クラスは、クライアントコードに触れることなく変更できます[柔軟性]。コードをテストしている間、実際のオブジェクトをモックオブジェクト[Testability] .bjectでモックオブジェクト[Testability]に置き換えることができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.