これは一般的なOOPの質問である可能性があります。使用法に基づいて、インターフェースと抽象クラスの一般的な比較を行いたかったのです。
いつインターフェースを使いたいのか、いつ抽象クラスを使いたいのか?
これは一般的なOOPの質問である可能性があります。使用法に基づいて、インターフェースと抽象クラスの一般的な比較を行いたかったのです。
いつインターフェースを使いたいのか、いつ抽象クラスを使いたいのか?
回答:
私はそれについての記事を書きました:
要約:
抽象クラスについて話すときは、オブジェクト型の特性を定義しています。オブジェクトとは何かを指定します。
インターフェースについて話し、提供することを約束する機能を定義するとき、それはオブジェクトが何ができるかについてのコントラクトを確立することについて話している。
Interfaces do not express something like "a Doberman is a type of dog and every dog can walk" but more like "this thing can walk"
。ありがとう
Use abstract classes and inheritance if you can make the statement “A is a B”. Use interfaces if you can make the statement “A is capable of [doing] as”
抽象クラスは、状態または機能を共有できます。インターフェイスは、状態または機能を提供するための約束にすぎません。優れた抽象クラスは、その機能または状態を共有できるため、書き換えが必要なコードの量を削減します。インターフェースには、共有する定義済みの情報がありません
個人的には、抽象クラスを書く必要はほとんどありません。
ほとんどの場合、抽象クラスが(誤)使用されているのを目にしています。これは、抽象クラスの作成者が「テンプレートメソッド」パターンを使用しているためです。
「テンプレートメソッド」の問題は、ほぼ常に再入可能であることです。「派生」クラスは、実装している基本クラスの「抽象」メソッドだけでなく、基本クラスのパブリックメソッドも認識します、ほとんどの場合それを呼び出す必要はありませんが。
(過度に簡略化された)例:
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);
}
したがって、ここでは、このクラスの作成者が一般的なアルゴリズムを記述しており、人々が独自の「フック」(この場合は「比較」メソッド)を提供することで「特殊化」して使用することを意図しています。
したがって、意図された使用法は次のようなものです:
class NameSorter : QuickSorter
{
public bool compare(object lhs, object rhs)
{
// etc.
}
}
これの問題は、次の2つの概念を過度に結合していることです。
上記のコードでは、理論的には、「compare」メソッドの作成者は、スーパークラスの「Sort」メソッドに再入可能にコールバックできます。
この不要なカップリングに支払う代償として、スーパークラスを変更するのは難しく、ほとんどのOO言語では実行時に変更することはできません。
代わりの方法は、代わりに "Strategy"デザインパターンを使用することです:
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());
}
抽象クラスを持っているときはいつでもこれを行うことができます...基本クラスと派生クラスの間に自然な再入可能な関係がある場合でも、通常はそれらを明示的にすることでお金がかかります。
最後に1つ考えてみましょう。関数型プログラミング言語では、「QuickSort」関数と「NameComparison」関数を使用して「NameSorting」関数を「構成」するだけで、このスタイルのプログラミングがさらに自然になります。少ないコードで。
JavaをOOP言語として見ている場合、
「インターフェースはメソッド実装を提供しません」は、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はインターフェースです。
アプリケーションに2つのクラスがあり、Serializable
インターフェースを実装していると仮定します。
Employee implements Serializable
Game implements Serializable
ここでは、経由どんな関係を確立することはできませんSerializable
との間のインターフェイスEmployee
とGame
異なる目的のために意図されています、。どちらも状態をシリアル化でき、比較はそこで終了します。
これらの投稿をご覧ください。
OK、これを自分で「グロッキー」にしたところ-これは素人の言葉です(私が間違っている場合は自由に修正してください)-私はこのトピックが間違っていることを知っていますが、誰かがいつかつまずくかもしれません...
抽象クラスを使用すると、設計図を作成したり、子孫すべてに所有させたいプロパティとメソッドを追加で構築(実装)したりできます。
一方、インターフェイスでは、特定の名前のプロパティやメソッドが、それを実装するすべてのクラスに存在することを宣言できるだけですが、実装方法は指定しません。また、クラスは多くのインターフェイスを実装できますが、拡張できる抽象クラスは1つだけです。インターフェースは、より高レベルのアーキテクチャツールです(デザインパターンを把握し始めると、より明確になります)。アブストラクトは両方の陣営に足を踏み入れ、汚れた作業の一部を実行することもできます。
なぜどちらを使うのか?前者は子孫のより具体的な定義を可能にし、後者はより大きな多形性を可能にします。この最後のポイントは、エンドユーザー/コーダーにとって重要です。エンドユーザー/コーダーは、この情報を利用して、ニーズに合わせてさまざまな組み合わせ/形状でAP I(インターフェース)を実装できます。
これは私にとって「電球」の瞬間だったと思います。インターフェースについては、著者の視点からではなく、プロジェクトに実装を追加したり、APIを拡張したりするチェーンの後半に登場するコーダーの視点から考えてください。
私の2セント:
インターフェイスは基本的にコントラクトを定義します。これは、実装クラスが遵守する必要がある(インターフェイスメンバーの実装)コントラクトです。コードは含まれていません。
一方、抽象クラスにはコードを含めることができ、継承クラスが実装する必要がある抽象としてマークされたメソッドがいくつかある場合があります。
私が抽象クラスを使用したまれな状況は、いくつかの特殊なクラスが継承する抽象基底クラスなど、継承するクラスがオーバーライドすることに興味がないかもしれないデフォルト機能がある場合です。
例(非常に初歩的な一つが!):のような抽象メソッドを持っている顧客と呼ばれる基本クラスで考えてみましょうCalculatePayment()
、CalculateRewardPoints()
とのようないくつかの非抽象メソッドをGetName()
、SavePaymentDetails()
。
RegularCustomer
およびなどの特殊化されたクラスGoldCustomer
は、Customer
基本クラスから継承し、独自のメソッドCalculatePayment()
とCalculateRewardPoints()
メソッドロジックを実装しますが、GetName()
およびSavePaymentDetails()
メソッドを再利用します。
古いバージョンを使用していた子クラスに影響を与えることなく、抽象クラス(つまり、非抽象メソッド)に機能を追加できます。一方、インターフェースにメソッドを追加すると、新しく追加されたインターフェースメンバーを実装する必要があるため、インターフェースを実装するすべてのクラスに影響します。
すべての抽象メンバーを持つ抽象クラスは、インターフェースに似ています。
あなたが心の中に明確な概念を持っているなら、非常に単純なことで何をすべきか。
抽象クラスは派生できますが、インターフェースは実装できます。2つの間にいくつかの違いがあります。抽象クラスを派生させると、派生クラスと基本クラスの関係は「is a」関係になります。たとえば、犬は動物、羊は動物です。これは、派生クラスが基本クラスからいくつかのプロパティを継承していることを意味します。
インターフェースの実装では、関係は「できる」です。たとえば、犬はスパイ犬になることができます。犬はサーカス犬になることができます。犬は競走犬になることができます。つまり、何かを取得するための特定のメソッドを実装します。
私は明確であることを望みます。
抽象クラスをいつ使用し、いつインターフェースを使用するかについての記事を書きました。それらの間には、「1つのIS-A ...と1つのCAN-DO ...」以外に多くの違いがあります。私にとって、これらは定型の答えです。どちらを使用するかについて、いくつかの理由を述べます。それが役に立てば幸い。
私はそれを置く最も簡単な方法は次のとおりだと思います:
共有プロパティ=>抽象クラス。
共有機能=>インターフェース。
簡潔に言うと...
抽象クラスの例:
public abstract class BaseAnimal
{
public int NumberOfLegs { get; set; }
protected BaseAnimal(int numberOfLegs)
{
NumberOfLegs = numberOfLegs;
}
}
public class Dog : BaseAnimal
{
public Dog() : base(4) { }
}
public class Human : BaseAnimal
{
public Human() : base(2) { }
}
動物には共有プロパティ(この場合は脚の数)があるため、この共有プロパティを含む抽象クラスを作成することは理にかなっています。これにより、そのプロパティを操作する一般的なコードを作成することもできます。例えば:
public static int CountAllLegs(List<BaseAnimal> animals)
{
int legCount = 0;
foreach (BaseAnimal animal in animals)
{
legCount += animal.NumberOfLegs;
}
return legCount;
}
インターフェースの例:
public interface IMakeSound
{
void MakeSound();
}
public class Car : IMakeSound
{
public void MakeSound() => Console.WriteLine("Vroom!");
}
public class Vuvuzela : IMakeSound
{
public void MakeSound() => Console.WriteLine("VZZZZZZZZZZZZZ!");
}
ここで、ブブゼラと車は完全に異なるものですが、機能を共有しています。したがって、ここではインターフェースが理にかなっています。さらに、プログラマーは、共通のインターフェースの下で音を出すものをグループ化することができます- IMakeSound
この場合。この設計では、次のコードを記述できます。
List<IMakeSound> soundMakers = new List<ImakeSound>();
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Vuvuzela());
foreach (IMakeSound soundMaker in soundMakers)
{
soundMaker.MakeSound();
}
それが何を出力するかわかりますか?
最後に、2つを組み合わせることができます。
組み合わせ例:
public interface IMakeSound
{
void MakeSound();
}
public abstract class BaseAnimal : IMakeSound
{
public int NumberOfLegs { get; set; }
protected BaseAnimal(int numberOfLegs)
{
NumberOfLegs = numberOfLegs;
}
public abstract void MakeSound();
}
public class Cat : BaseAnimal
{
public Cat() : base(4) { }
public override void MakeSound() => Console.WriteLine("Meow!");
}
public class Human : BaseAnimal
{
public Human() : base(2) { }
public override void MakeSound() => Console.WriteLine("Hello, world!");
}
ここでは、すべてBaseAnimal
のが音を出すことを要求していますが、その実装はまだわかりません。このような場合、インターフェースの実装を抽象化して、その実装をサブクラスに委任できます。
最後に、抽象クラスの例では、さまざまなオブジェクトの共有プロパティを操作でき、インターフェイスの例では、さまざまなオブジェクトの共有機能を呼び出すことができたことを思い出してください。この最後の例では、両方を行うことができます。
インターフェイスよりも抽象クラスを好む場合は?
抽象クラスよりもインターフェースを好む場合は?
クラスは1つの基本クラスからのみ継承できるため、抽象クラスを使用してクラスのグループにポリモーフィズムを提供する場合は、それらすべてがそのクラスから継承する必要があります。抽象クラスは、すでに実装されているメンバーを提供する場合もあります。したがって、抽象クラスを使用して一定量の同一の機能を保証できますが、インターフェースを使用して保証することはできません。
コンポーネントにポリモーフィズムを提供するためにインターフェイスまたは抽象クラスのどちらを使用するかを決定するのに役立ついくつかの推奨事項を次に示します。
コピー元:http :
//msdn.microsoft.com/en-us/library/scsyfw1d%28v=vs.71%29.aspx
これらのステートメントのいずれかが状況に当てはまる場合は、抽象クラスの使用を検討してください。
これらのステートメントのいずれかが状況に当てはまる場合は、インターフェースの使用を検討してください。
答えは言語によって異なります。たとえば、Javaでは、クラスは複数のインターフェースを実装(継承)できますが、継承する抽象クラスは1つだけです。したがって、インターフェースにより柔軟性が高まります。しかし、これはC ++では当てはまりません。
私にとっては、多くの場合、インターフェースを使用します。しかし、私はいくつかのケースで抽象クラスを好む。
オブジェクト指向のクラスは、一般的に実装を指します。実装の詳細を子に強制したい場合は、抽象クラスを使用します。それ以外の場合は、インターフェースを使用します。
もちろん、抽象クラスは、実装を強制するだけでなく、多くの関連クラス間で特定の詳細を共有するのにも役立ちます。
基本的な実装を提供する場合は、抽象クラスを使用します。
Javaでは、1つの(抽象的な)クラスから継承して機能を「提供」し、多くのインターフェースを実装して機能を「保証」できます。
インターフェースが抽象クラスよりもうまく機能する興味深い場所の1つは、(関連するまたは関連しない)オブジェクトのグループに機能を追加する必要がある場合です。それらに基本抽象クラスを与えることができない場合(たとえば、それらはsealed
親であるか、すでに親を持っている)、代わりにダミー(空)インターフェースを与えて、そのインターフェースの拡張メソッドを記述するだけです。
これは非常に難しい電話です...
1つのポインタ:オブジェクトは多くのインターフェイスを実装できますが、オブジェクトは1つの基本クラスしか継承できません(c#のような最近のOO言語では、C ++には複数の継承があることはわかっていますが、それは不快ではありませんか?)
抽象クラスは実装を持つことができます。
インターフェイスには実装がなく、一種のコントラクトを定義するだけです。
また、言語に依存するいくつかの違いがある可能性があります。たとえば、C#には複数の継承はありませんが、複数のインターフェイスをクラスに実装できます。