これは一般的な OOP の質問かもしれません。使用法に基づいて、インターフェイスと抽象クラスの一般的な比較を行いたかったのです。
いつインターフェイスを使用したいのか、いつ抽象クラスを使用したいのか?
これは一般的な OOP の質問かもしれません。使用法に基づいて、インターフェイスと抽象クラスの一般的な比較を行いたかったのです。
いつインターフェイスを使用したいのか、いつ抽象クラスを使用したいのか?
私はそれについての記事を書きました:
要約:
抽象クラスについて話すときは、オブジェクト型の特性を定義しています。オブジェクトが何であるかを指定します。
インターフェイスについて話し、提供することを約束する機能を定義するときは、オブジェクトが実行できることについてのコントラクトを確立することについて話します。
抽象クラスは、共有状態または機能を持つことができます。インターフェースは、状態または機能を提供することを約束するだけです。優れた抽象クラスは、機能や状態を共有できるため、書き直さなければならないコードの量を減らします。インターフェイスには、共有する定義済みの情報がありません
個人的には、抽象クラスを書く必要はほとんどありません。
ほとんどの場合、抽象クラスが(誤)使用されているのを目にします。これは、抽象クラスの作成者が「テンプレートメソッド」パターンを使用しているためです。
「テンプレートメソッド」の問題は、ほとんどの場合、ある程度再入可能であるということです。「派生」クラスは、実装している基本クラスの「抽象」メソッドだけでなく、基本クラスのパブリックメソッドについても知っています。 、ほとんどの場合、それらを呼び出す必要はありませんが。
(過度に単純化された)例:
abstract class QuickSorter
{
public void Sort(object[] items)
{
// implementation code that somewhere along the way calls:
bool less = compare(x,y);
// ... more implementation code
}
abstract bool compare(object lhs, object rhs);
}
したがって、ここで、このクラスの作成者は一般的なアルゴリズムを作成し、独自の「フック」(この場合は「compare」メソッド)を提供することで「特殊化」することで、人々がそれを使用できるようにします。
したがって、使用目的は次のようになります。
class NameSorter : QuickSorter
{
public bool compare(object lhs, object rhs)
{
// etc.
}
}
これに伴う問題は、2つの概念を過度に結合していることです。
上記のコードでは、理論的には、「compare」メソッドの作成者は、スーパークラスの「Sort」メソッドに再度コールバックできます...実際には、これを実行する必要はありません。
この不要な結合に支払う代償は、スーパークラスを変更するのが難しく、ほとんどのオブジェクト指向言語では、実行時に変更することが不可能であるということです。
別の方法は、代わりに「戦略」デザインパターンを使用することです。
interface IComparator
{
bool compare(object lhs, object rhs);
}
class QuickSorter
{
private readonly IComparator comparator;
public QuickSorter(IComparator comparator)
{
this.comparator = comparator;
}
public void Sort(object[] items)
{
// usual code but call comparator.Compare();
}
}
class NameComparator : IComparator
{
bool compare(object lhs, object rhs)
{
// same code as before;
}
}
だから今注意してください:私たちが持っているのはインターフェースとそれらのインターフェースの具体的な実装だけです。実際には、高レベルのOO設計を行うために他に何も必要ありません。
「QuickSort」クラスと「NameComparator」を使用して「名前の並べ替え」を実装したという事実を「隠す」ために、ファクトリメソッドをどこかに記述します。
ISorter CreateNameSorter()
{
return new QuickSorter(new NameComparator());
}
抽象クラスがあるときはいつでもこれを行うことができます...基本クラスと派生クラスの間に自然な再入可能な関係がある場合でも、通常、それらを明示的にすることは有益です。
最後に考えてみましょう。上記で行ったのは、関数型プログラミング言語で「QuickSort」関数と「NameComparison」関数を使用して「NameSorting」関数を「作成」することだけです。このスタイルのプログラミングはさらに自然になります。より少ないコードで。
Java を OOP 言語として見ている場合、
「interface does not provide method implementation」は、Java 8 の起動で無効になりました。現在、Java はデフォルト メソッドのインターフェイスでの実装を提供しています。
簡単に言えば、私は使いたい
インターフェイス:複数の無関係なオブジェクトによってコントラクトを実装すること。「 HAS A」機能を提供します。
抽象クラス:複数の関連オブジェクト間で同じまたは異なる動作を実装すること。「 IS A」関係を確立します。
OracleのWeb サイトinterface
には、とabstract
クラスの主な違いが示されています。
次の場合は、抽象クラスの使用を検討してください。
次の場合は、インターフェイスの使用を検討してください。
Serializable
インターフェイスを実装できます。例:
抽象クラス ( IS A関係)
Readerは抽象クラスです。
BufferedReaderはReader
FileReaderはReader
FileReader
BufferedReader
共通の目的で使用されます: データの読み取り、およびそれらはクラスを介して関連付けられますReader
。
インターフェイス ( HAS A機能)
Serializableはインターフェースです。
アプリケーションにSerializable
インターフェイスを実装する 2 つのクラスがあるとします。
Employee implements Serializable
Game implements Serializable
ここでは、目的が異なる とのSerializable
間のインターフェースを介して関係を確立することはできません。どちらも状態をシリアライズすることができ、比較はそこで終了します。Employee
Game
これらの投稿を見てください:
私の2セント:
インターフェースは基本的に、実装クラスが従わなければならない契約を定義します(インターフェースメンバーを実装します)。コードは含まれていません。
一方、抽象クラスにはコードを含めることができ、継承クラスが実装する必要がある抽象としてマークされたメソッドがいくつかある場合があります。
私が抽象クラスを使用したまれな状況は、いくつかの特殊なクラスが継承する抽象基本クラスなど、継承クラスがオーバーライドすることに興味がないかもしれないいくつかのデフォルト機能がある場合です。
CalculatePayment()
例 (非常に初歩的なもの!): のような抽象メソッドと、のようなCalculateRewardPoints()
いくつかの非抽象メソッドを持つ Customer という基本クラスを考えてみましょう。GetName()
SavePaymentDetails()
RegularCustomer
andのような特殊化されたクラスGoldCustomer
は、基本クラスから継承し、Customer
独自のCalculatePayment()
andメソッド ロジックを実装しますが、 andメソッドCalculateRewardPoints()
を再利用します。GetName()
SavePaymentDetails()
古いバージョンを使用していた子クラスに影響を与えることなく、抽象クラス (非抽象メソッド) に機能を追加できます。インターフェイスにメソッドを追加すると、新しく追加されたインターフェイス メンバーを実装する必要があるため、それを実装するすべてのクラスに影響します。
すべての抽象メンバーを持つ抽象クラスは、インターフェイスに似ています。
OK、これを自分で「グロック」しただけです-ここでは素人の言葉で言います(私が間違っている場合は私を訂正してください)-私はこのトピックがooooooldであることを知っていますが、誰か他の人がいつかそれに遭遇するかもしれません...
抽象クラスを使用すると、ブループリントを作成でき、さらに、そのすべての子孫に所有させたいプロパティとメソッドを構築(実装)できます。
一方、インターフェイスでは、指定された名前のプロパティやメソッドを、それを実装するすべてのクラスに存在させることを宣言することしかできませんが、実装方法は指定していません。また、クラスは多くのインターフェースを実装できますが、拡張できるのは1つの抽象クラスのみです。インターフェイスは、より高レベルのアーキテクチャツールです(デザインパターンを把握し始めるとより明確になります)。Abstractは両方の陣営に足を踏み入れており、汚い作業の一部も実行できます。
なぜ一方を他方の上に使用するのですか?前者は子孫のより具体的な定義を可能にします-後者はより大きなポリモーフィズムを可能にします。この最後のポイントは、エンドユーザー/コーダーにとって重要です。エンドユーザー/コーダーは、この情報を利用して、ニーズに合わせてさまざまな組み合わせ/形状でAP I(インターフェース)を実装できます。
これは私にとって「電球」の瞬間だったと思います。インターフェースについては、作成者の見方からではなく、プロジェクトに実装を追加したり、APIを拡張したりするチェーンの後半に来るコーダーの見方から考えてください。
コンセプトが頭の中にはっきりしていれば、いつ何をするかは非常に簡単です。
抽象クラスは派生できますが、インターフェイスは実装できます。両者にはいくつかの違いがあります。Abstract クラスを派生させると、派生クラスと基底クラスの関係は「is a」関係になります。たとえば、犬は動物、羊は動物です。これは、派生クラスが基本クラスからいくつかのプロパティを継承していることを意味します。
一方、インターフェースの実装に関しては、その関係は「可能」です。たとえば、犬はスパイ犬になることができます。犬はサーカスの犬になることができます。犬は競走犬になることができます。つまり、何かを取得するために特定のメソッドを実装するということです。
私がはっきりしていることを願っています。
1.無関係なクラスに共通の機能を提供するものを作成している場合は、インターフェイスを使用します。
2.階層的に密接に関連するオブジェクトに対して何かを作成する場合は、抽象クラスを使用します。
抽象クラスを使用する場合とインターフェイスを使用する場合の記事を書きました。「IS-Aが1つ...とCAN-DOが1つ...」以外にも、それらの間にははるかに多くの違いがあります。私にとって、それらは既製の答えです。それらのいずれかをいつ使用するかについて、いくつかの理由について言及します。それが役に立てば幸い。
クラスは 1 つの基本クラスからしか継承できないため、抽象クラスを使用してクラスのグループにポリモーフィズムを提供する場合は、すべてそのクラスから継承する必要があります。抽象クラスは、既に実装されているメンバーを提供する場合もあります。したがって、抽象クラスではある程度の同一機能を確保できますが、インターフェースではできません。
コンポーネントにポリモーフィズムを提供するためにインターフェイスと抽象クラスのどちらを使用するかを決定する際に役立つ推奨事項をいくつか紹介します。
からコピー:
http://msdn.microsoft.com/en-us/library/scsyfw1d%28v=vs.71%29.aspx
基本的な実装を提供する場合は、抽象クラスを使用します。
答えは言語によって異なります。たとえば、Javaでは、クラスは複数のインターフェースを実装(継承)できますが、1つの抽象クラスからのみ継承します。したがって、インターフェースはより柔軟性を提供します。しかし、これはC++には当てはまりません。
私にとっては、多くの場合、インターフェースを使用します。しかし、場合によっては抽象クラスを好みます。
オブジェクト指向のクラスは一般に実装を指します。いくつかの実装の詳細を子に強制したい場合は、抽象クラスを使用します。それ以外の場合は、インターフェイスを使用します。
もちろん、抽象クラスは実装を強制するだけでなく、関連する多くのクラス間で特定の詳細を共有するのにも役立ちます。
Javaでは、1つの(抽象)クラスから継承して機能を「提供」し、多くのインターフェースを実装して機能を「保証」することができます。
これは非常に難しい電話になる可能性があります...
私が与えることができる1つのポインタ:オブジェクトは多くのインターフェイスを実装できますが、オブジェクトは1つの基本クラスしか継承できません(c#のような最新のオブジェクト指向言語では、C ++には多重継承があることを知っていますが、それは嫌われていませんか?)
純粋に継承に基づいて、明確に子孫の抽象的な関係(つまり、動物->猫)を定義している場合、および/または仮想または非公開プロパティ、特に共有状態(インターフェイスがサポートできない)の継承を要求する場合は、抽象を使用します)。
ただし、可能な場合は継承よりも(依存性注入による)構成を優先する必要があります。また、コントラクトであるインターフェイスは、Abstractsでは不可能な方法で、単体テスト、関心の分離、および(言語が異なる)多重継承をサポートすることに注意してください。
インターフェイスが抽象クラスよりもうまく機能する興味深い場所の 1 つは、(関連または非関連の) オブジェクトのグループに追加の機能を追加する必要がある場合です。それらに基本抽象クラスを与えることができない場合 (たとえば、sealed
親である、または既に親を持っている)、代わりにダミー (空の) インターフェイスを与えて、そのインターフェイスの拡張メソッドを単純に記述することができます。
抽象クラスは実装を持つことができます。
インターフェイスには実装がなく、一種のコントラクトを定義するだけです。
言語に依存する違いもいくつかあります。たとえば、C#には多重継承はありませんが、クラスに複数のインターフェイスを実装できます。
基本的な経験則は次のとおりです。「名詞」には抽象クラスを使用し、 「動詞」にはインターフェースを使用します
例:car
は抽象クラスでありdrive
、インターフェイスにすることができます。