web-dev-qa-db-ja.com

抽象クラス、インターフェース、およびそれらをいつ使用するかの違いは何ですか

最近、私はOOPに頭を抱え始めており、抽象クラスとインターフェースの違いについて読むほど、混乱するようになりました。これまでのところ、どちらもインスタンス化できません。インターフェースは多かれ少なかれ構造的な設計図であり、コードを部分的に実装できるため、スケルトンと要約は異なります。

私の特定の状況を通じて、これらについて詳しく知りたいと思います。もう少し背景情報が必要な場合は、最初の質問へのリンクを次に示します。 新しいクラスに適したデザインモデルは何ですか?

ここに私が作成した2つのクラスがあります:

_class Ad {
    $title;
    $description
    $price;

    function get_data($website){  }

    function validate_price(){  }
 }


class calendar_event {
    $title;
    $description

    $start_date;

    function get_data($website){ //guts }

    function validate_dates(){ //guts }
 }
_

ご覧のとおり、これらのクラスはほとんど同じです。ここには示されていませんが、他の関数like get_Zip()save_to_database()があり、これらはクラス全体で共通です。他のクラスCarsおよびPetsも追加しました。これらのクラスにはすべての一般的なメソッドがあり、もちろんそれらのクラスに固有のプロパティ(走行距離、重量など)があります。

今私は[〜#〜] dry [〜#〜]の原則に違反し、複数のファイルにわたって同じコードを管理および変更しています。ボートや馬などのクラスを増やしたいと思っています。

これは、インターフェイスまたは抽象クラスを使用する場所ですか?抽象クラスについて理解していることから、抽象クラスに組み込まれているすべての共通要素を持つテンプレートとしてスーパークラスを使用し、将来のクラスで特に必要な項目のみを追加します。例えば:

_abstract class content {
    $title;
    $description


    function get_data($website){  }

    function common_function2() { }
    function common_function3() { }
 }


class calendar_event extends content {

    $start_date;

    function validate_dates(){  }
 }
_

または、インターフェースを使用しますが、これらは非常に似ているため、整合性の理由で各サブクラスが使用を強制される構造を作成し、そのクラスをそれぞれの責任を負うように仕上げるエンド開発者に任せます共通機能の詳細さえ。私の考えでは、特定のクラスのニーズに応じて、いくつかの「共通」機能を将来調整する必要があるかもしれません。

上記のすべてにもかかわらず、抽象クラスとインターフェースの全体とその理由を完全に誤解していると思われる場合は、この方向で考えるのをやめ、適切な前進方法を提案するために、必ず有効な答えを出してください!

ありがとう!

15
user66662

素人の言葉で:

Interfacesは、「実行できる/処理できる」タイプの関係用です。

抽象(および具象)クラスは、 "is a"の関係です。

これらの例を見てください:

_class Bird extends Animal implements Flight;
class Plane extends Vehicle implements Flight, AccountableAsset;
class Mosquito extends Animal implements Flight;
class Horse extends Animal;
class RaceHorse extends Horse implements AccountableAsset;
class Pegasus extends Horse implements Flight;
_

BirdMosquitoおよびHorseareAnimals。それらは関連しています。 eat(), metabolize() and reproduce()などの共通のメソッドをAnimalから継承します。おそらく、これらのメソッドをオーバーライドして少し追加しますが、metabolizeGlucose().のように、Animalに実装されているデフォルトの動作を利用しています。

Planeは、BirdMosquitoまたはHorseとは関係ありません。

Flightは、BirdPlaneのような、類似していない無関係なクラスによって実装されます。

AccountableAssetは、PlaneRaceHorseのような、類似していない関連のないクラスによっても実装されます。

HorseはFlightを実装していません。

ご覧のとおり、classes(抽象または具象)階層の構築に役立ちますで、階層の上位レベルから下位レベルにコードを継承できます。理論的には、階層が下になるほど、動作はより特殊化されますが、すでに処理されている多くのことについて心配する必要はありません。

Interfaces一方、階層を作成しませんが、それらは役立ちます階層全体で特定の動作を均質化するしたがって、特定のコンテキストの階層からそれらを抽象化できます。

たとえば、プログラムがAccountableAssetsまたはRaceHorsesであるかどうかに関係なく、Planesのグループの値を合計することができます。

26

2つの違いを認識しているように見えるので、答えを論理的に推測できます。

インターフェース共通の契約を定義します。 IAnimalと呼ばれるインターフェイスなど、すべての動物がEat()、Move()、Attack()などの機能を共有します。すべての動物が同じ機能を共有しますが、それらのすべてまたはほとんどが異なる方法(実装)でそれ。

抽象クラ​​スは、共通の実装とオプションで共通の契約を定義します。たとえば、単純な電卓は、すべての基本的な論理演算子とビットごとの演算子を実装し、ScientificCalculatorやGraphicalCalculatorなどによって拡張される抽象クラスと見なすことができます。

共通の実装がある場合は、必ず、拡張する抽象クラスに機能をカプセル化してください。私は0 PHP=の経験を持っていますが、非定数フィールドでインターフェースを作成することはできないと思います。フィールドがインスタンスクラス間で共通である場合、抽象クラスを使用する必要があります。ゲッターとセッターを介してそれらへのアクセスを定義しない限り。

また、グーグルでは結果に事欠かないようです。

13
Dante

短い話です。抽象クラスは、継承クラス内にある必要のあるメソッドのテンプレートを提供するという点でインターフェイスに非常に似ていますが、大きな違いがあります。クラスはメソッドの完全なデフォルトコードを持つことができ、詳細のみをオーバーライドする必要がある場合があります。 -インターフェイスはアクセス修飾子を持つことができません。 -インターフェイスにフィールドを含めることはできません。 -クラスは複数のクラスを継承できませんが、複数のインターフェースを継承できます。 -また、クラスは階層構造を提供するので、特定のクラスから派生したクラスだけが、抽象クラスのガイドラインに従う必要があります:オブジェクト->特定のオブジェクト->非常に特定のオブジェクト。一方、インターフェースは誰でもどこでも継承できます。

私の考えでは、抽象クラスはコードのデフォルト実装をすぐに提供できるという点でより一般的ですが、特定のクラスを標準化する必要がある大規模プロジェクトでは、インターフェースが便利になる場合があります。

それが役に立てば幸いですが、このオンラインにはたくさんの情報があります、レオ

3

まず、インターフェースと抽象クラスの両方を提供することが多いことを理解する必要があります。この理由、および2つのコアの主な違いは、異なるコードを再利用できるため、異なる問題を解決できるためです。

インターフェイスを使用すると、さまざまな実装でクライアントコードを再利用できます。 get_data($ website)クラスのクライアントは、$ titleまたは$ descriptionアイテムを気にしません。コンテンツにデータをロードするように指示するだけです。さまざまな種類のコンテンツがあり、その中には$ descriptionが必要なものと不要なものがある場合は、子クラスの署名のみを指定するContentInterfaceクラスを提供できます。これで、クライアントは、どのように動作するかを正確に知らなくても、さまざまなコンテンツをいくつでも持つことができます。 Liskov Substitution Principle は、このアイデアを研究するために読んでおくとよいでしょう。私も好きです 叔父ボブの トピックについて書く。 インターフェースは単体テストにとって非常に重要であり、インターフェースを作成することを学ぶのは良い習慣です。

抽象クラスを使用すると、共通の祖先を共有するクラスのセット全体で共通の実装の詳細を再利用できます。あなたの質問では、抽象クラスから実装を継承する理由をうまく理解しているようです。基本クラスの内部に依存することは依然として危険です-カプセル化に違反し、基本クラスの特定の実装の詳細に依存する子を作成することは非常に簡単です。 テンプレートメソッドパターン は、カプセル化に違反せずに基本クラスを使用する方法の一般的で健全な例を提供します。

したがって、私が示したように、クラス階層のクライアントにインターフェイスを提供することがよくあるため、クライアントコードに影響を与えずに実装を安全に変更できます。これにより、クライアントは、インターフェイスを継承する Mock Objects を使用して単体テストを作成できます。また、共通のロジックを再利用したり、子クラスのセマンティクスを強制したりする抽象クラスも提供します。

3
Ben

違いは微妙ですが、はっきりしています。インターフェイスは、ポリモーフィックな動作に関するものです。抽象クラスは、再利用と多態的な振る舞いに関するものです。

再利用や多態的な振る舞いに重点を置きたい場合は、抽象クラスを選択してください。たとえば、さまざまなタイプの従業員にはさまざまな規定がありますが、すべてがいくつかの共通事項を受け取ります。したがって、共通性は基本抽象クラスEmployeeで表現でき、その違いはManagerWorkerなどの派生クラスで実装できるため、抽象クラスはそれを表すのに適しています。

ポリモーフィックな動作のみを強調したい場合は、インターフェースを選択します。インターフェイスはコントラクト、つまりオブジェクトまたは階層が特定の動作に準拠していることを示すものです。たとえば、すべての従業員には休暇規定がありますが、異なるタイプの従業員には異なるタイプの規定があります。そのため、従業員のタイプごとに異なる休暇計算機が必要です。すべてのタイプの従業員がLeaveCalculatorインターフェースとCalculate()動作を異なる方法で実装できるため、ここではインターフェースが適切な選択です。

3
theD