207

インターフェースそれを実装するクラスのメソッドを定義するコードを作成することを許可します。ただし、これらのメソッドにコードを追加することはできません。

抽象クラスメソッドにコードを追加しながら、同じことを実行できるようにします。

抽象クラスで同じ目標を達成できるのであれば、インタフェースの概念が必要なのはなぜでしょうか。

私はそれがC ++からJavaまでのオブジェクト指向理論に関係していると言われました。この概念はJavaでは役に立ちますが、PHPでは役立ちませんか?抽象クラスでプレースホルダーが散らばってしまうのを防ぐための方法にすぎませんか。私は何かが足りないのですか?


  • これを読む必要があります。stackoverflow.com/a/384067/14673 - Luc M
  • 確かにそれは精神的な援助、そしてコミュニケーションの援助です。インターフェースは、あなたのAPIが実装することを知らなくても読むことができる抽象的な方法であなたのAPIが公開するサービスを一緒にまとめているので、あなたのAPIのための優れた教育ツールとして機能します。これは高価ですが、インターフェースに慣れている人はすでにクラスを必要とせずに直接関数を使用できるため、ポインタを節約できます。言語で定義されたインターフェースがないと、多くのプログラマーのために前もって計画を立てるのが難しいことがあります。多くの場合、紙の上ではなくインターフェースを使用して設計することを好みます。 - Dmitry

14 답변


136

インターフェースの全体的な要点は、クラスに複数のインターフェースを実装することを強いる柔軟性を与えることですが、それでも多重継承を許可しません。複数のクラスから継承することに関する問題は多岐にわたります。ウィキペディアそれのページはそれらをかなりよくまとめます。

インターフェースは妥協です。多重継承に関する問題の多くは抽象基底クラスには当てはまりません。そのため、最近のほとんどの言語は多重継承を無効にしながら抽象基底クラスのインターフェイスを呼び出し、クラスが望むだけのものを「実装」できるようにします。


  • *インタフェースは、実装がゼロのクラスの設計を提供するとも言えます。 *抽象クラスはいくつかの実装といくつかのデザインを提供します。抽象クラスは、子クラスが実装の類似点をいくつか共有しているが、実装によっては異なる場合に最も便利です。 - Jrgns
  • @Craig、多重継承に固有の問題はありません。現在の言語では、それらを正しく実装するのに十分なほど強力ではありません。 「問題」実際にはかなり簡単に修正できます。たとえば、同じ名前の継承された関数に対する明示的な継承パスを指定することで、ダイヤモンドのジレンマを解決できます。 - Pacerier
  • それは、インターフェイスがまったく関係ないということではありません。これは多重継承に対する妥協ではなく、実装するオブジェクトと他のオブジェクト/メソッドが消費する概念的かつ抽象的な契約を作成することに関するものです。インターフェースは多相のためのツールであり、直接の継承のためのものではありません。 - Madara Uchiha

120

この概念は、オブジェクト指向プログラミングのあらゆる面で役に立ちます。私にとって、私はインターフェースを契約として考えています。私のクラスとあなたのクラスがこのメソッドシグネチャ契約に同意する限り、「インタフェース」をとることができます。抽象クラスに関しては、私はいくつかのメソッドをスタブアウトするより多くの基本クラスと見ています、そして私は詳細を記入する必要があります。


  • これは私にそれを少し理解させました。名前空間と似ているため、共有コードの方が使いやすく、競合もありません。 2人の人が同じベースでクラスを作るのは簡単ですよね。 - RedClover
  • 'インターフェースの一般的な概念を区別する必要はありません。 PHPのような言語の具体的なインターフェースから?たとえば、どの関数にも「インターフェース」があります。これはあなたがそれをどのように使うかを定義し、その実装を隠します。そのような「契約上の」特別な言語機能は必要ありません。したがって、言語機能は他のもの(または追加のもの)のためのものでなければなりません。 - DaveInCaz

56

すでに抽象クラスがある場合、なぜあなたはインターフェースが必要なのでしょうか?多重継承を防ぐ(既知の複数の問題を引き起こす可能性がある)。

そのような問題の1つ:

「ダイヤモンドの問題」(「致命的なダイヤモンド」とも呼ばれます)   死 ")は、2つのクラスBとCが継承するときに生じるあいまいさです。   Aから、クラスDはBとCの両方から継承します。メソッドがある場合   AではBとCがオーバーライドし、Dはそれをオーバーライドしません。   Dはどのバージョンのメソッドを継承しているのでしょうか。

ソース:https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem

なぜ/いつインターフェースを使うのですか?例...世界中のすべての車が同じインターフェース(メソッド)を持っています...AccelerationPedalIsOnTheRight()BrakePedalISOnTheLeft()。各自動車ブランドが他のブランドと異なるこれらの「方法」を持つことを想像してみてください。 BMWの右側にブレーキがあり、ホンダの左側にブレーキがあるはずです。人々は、これらの「方法」が異なるブランドの車を買うたびにどのように機能するかを学ぶ必要があります。だからこそ、同じインターフェイスを複数の場所に配置することをお勧めします。

インターフェースはあなたにとって何をするのでしょうか(なぜか誰かがそれを使うだろう)。 インターフェースは、あなたが「間違い」をするのを防ぎます(それは、特定のインターフェースを実装するすべてのクラスが、そのインターフェースにあるメソッドをすべて持っていることを保証します)。

// Methods inside this interface must be implemented in all classes which implement this interface.
interface IPersonService
{   
    public function Create($personObject);
}

class MySqlPerson implements IPersonService
{
    public function Create($personObject)
    {
        // Create a new person in MySql database.
    }
}

class MongoPerson implements IPersonService
{
    public function Create($personObject)
    {
        // Mongo database creates a new person differently then MySQL does. But the code outside of this method doesn't care how a person will be added to the database, all it has to know is that the method Create() has 1 parameter (the person object).
    }
}

このように、Create()methodは常に同じ方法で使用されます。私たちが使っているかどうかは関係ありません。MySqlPersonクラスまたはMongoPersonクラス。私たちがどのようにメソッドを使っているかは変わりません(インタフェースは変わりません)。

たとえば、これは次のように使用されます(コードの至る所で)。

new MySqlPerson()->Create($personObject);
new MongoPerson()->Create($personObject);

このように、このようなことは起こり得ない:

new MySqlPerson()->Create($personObject)
new MongoPerson()->Create($personsName, $personsAge);

複数の異なるインターフェースよりも、1つのインターフェースを覚えてどこでも同じインターフェースを使用する方がはるかに簡単です。

このようにして、Create()methodは、このメソッドを呼び出す "outside"コードに影響を与えずに、クラスごとに異なる可能性があります。外部のコードが知るべきことは、メソッドがCreate()1個のパラメータ($personObjectこれは、外部コードがこのメソッドを使用または呼び出す方法です。外部のコードは、メソッド内で何が起こっているのかを気にしません。それを使う方法やそれを呼び出す方法を知っているだけです。

あなたはインターフェースなしでも同様にこれをすることができます、しかし、あなたがインターフェースを使うならば、それは(あなたがミスをするのを防ぐので)より安全です。インターフェースはあなたにメソッドがCreate()インタフェースを実装するすべてのクラスで同じシグネチャ(同じ型と同じ数のパラメータ)を持ちます。このようにして、あなたはそのクラスを実装するすべてのクラスを確実にすることができます。IPersonServiceインタフェースは、メソッドがありますCreate()(この例では)パラメータは1つだけ必要です($personObject)を呼び出す/使用する。

インターフェースを実装するクラスは、そのインターフェースが持つ/持つすべてのメソッドを実装しなければなりません。

二度と繰り返さないようにしてください。


21

インターフェースと抽象クラスの使用の違いは、言語自体による強制よりも、私にとってはコード編成と関係があります。意図したデザインパターンの範囲内に収まるように他の開発者が作業するためのコードを準備するときに、私はそれらを頻繁に使用します。インターフェースは一種の「契約による設計」であり、それによってあなたのコードはあなたが頼りにしていないコードから来るかもしれないAPI呼び出しの規定されたセットに応答することに同意します。

抽象クラスからの継承は "is"の関係ですが、それは必ずしも望んでいることではありません。そしてインターフェースの実装は "行為のような"行為のようなものです。この違いは、特定の状況では非常に重要です。

たとえば、他にも多くのクラスが拡張されている抽象クラスAccountがあるとします(アカウントの種類など)。その型グループにのみ適用可能な特定のメソッドセットがあります。ただし、これらのアカウントサブクラスの中には、Versionable、Listable、またはEditableを実装しているため、それらのAPIを使用することを想定しているコントローラにスローすることができます。コントローラは、それがどんな種類のオブジェクトであるかを気にしません。

これとは対照的に、Accountから継承しないオブジェクト、たとえばUser抽象クラスを作成し、ListableとEditableを実装することはできますが、Versionableは実装しないので、ここでは意味がありません。

このように、FooUserサブクラスはアカウントではありませんが、DOESはEditableオブジェクトのように機能します。同様に、BarAccountはAccountから継承されていますが、Userサブクラスではありませんが、Editable、Listable、およびVersionableを実装しています。

これらのEditable、Listable、およびVersionableのAPIをすべて抽象クラス自体に追加すると、雑然とした醜いものになるだけでなく、AccountとUserの共通インターフェイスが重複するか、UserオブジェクトにVersionableの実装を強制することになります。例外。


  • これはここです。それらを拡張または上書きすることなく、開発者があなたのメソッドを使用するように厳格に強制する - Nick

20

インターフェースは基本的にあなたが作成できるものの青写真です。それらはクラスがどんなメソッドを定義しているか持つ必要がありますしかし、これらの制限の外に追加のメソッドを作成することができます。

メソッドにコードを追加できないということが、どういう意味なのかわかりません。あなたは抽象クラスまたはそれを拡張するクラスにインターフェースを適用していますか?

抽象クラスに適用されるインタフェース内のメソッドは、その抽象クラスに実装する必要があります。ただし、そのインターフェースを拡張クラスに適用すると、メソッドは拡張クラスに実装するだけで済みます。私はここで間違っている可能性があります - 私は私ができるべき/するべきであるほど私はインターフェースを使用しません。

私はインターフェースを外部の開発者のためのパターン、あるいは物事が正しいことを保証するための追加のルールセットとして常に考えていました。


  • PHPでは、インターフェースはメソッド宣言のみを含み、実際の実装は含みません。ただし、抽象クラスでは、「コードを追加する」ことができます。それを拡張するクラスによって継承されるメソッドに。私はこの違いがmkが言及していたものだと思います。 - nocash

9

あなたはPHPでインターフェースを使うでしょう:

  1. 実装を隠す - オブジェクトのクラスへのアクセスプロトコルを確立し、そのオブジェクトを使用していたすべての場所をリファクタリングすることなく基礎となる実装を変更する
  2. 型を確認するには - パラメータに特定の型があることを確認する場合と同様に$object instanceof MyInterface
  3. 実行時にパラメータチェックを実行する
  4. 複数の動作を1つのクラスに実装する(複雑な型を構築する)

    CarクラスはEngineInterface、BodyInterface、SteeringInterfaceを実装します。 だから、Carオブジェクトcastart()stop()(EngineInterface)またはgoRight()goLeft()(ステアリングインターフェース)

    そして今私が考えることができない他の事

    4つ目は、抽象クラスでは対応できないという最も明白な使用例です。

    Javaで考えることから:

    「これは、この特定のインターフェースを実装するすべてのクラスがどのように見えるかということです。」したがって、特定のインターフェースを使用するコードは、そのインターフェースに対してどのメソッドを呼び出すことができるかを知っています。そのため、インターフェイスはクラス間の「プロトコル」を確立するために使用されます。


9

インタフェースは、クラスが拡張できる基礎としてではなく、必要な機能のマップとして存在します。

以下は、抽象クラスが収まらないインターフェイスの使用例です。

ユーザーが外部ソースからカレンダーデータをインポートできるようにするカレンダーアプリケーションがあるとしましょう。各タイプのデータソース(ical、rss、atom、json)のインポートを処理するクラスを作成します。これらの各クラスは、私のアプリケーションがデータを取得するために必要な共通のパブリックメソッドを確実に持つ共通インターフェースを実装します。

<?php

interface ImportableFeed 
{
    public function getEvents();
}

次に、ユーザーが新しいフィードを追加すると、そのフィードの種類を識別し、その種類用に開発されたクラスを使用してデータをインポートできます。特定のフィードのデータをインポートするために作成された各クラスは完全に異なるコードを持ちます。それ以外の点では、クラスが私のアプリケーションで使用できるインターフェースを実装する必要があるという事実以外はクラス間の類似点はほとんどありません。抽象クラスを使用するのであれば、getEvents()メソッドをオーバーライドしていないためにこの場合はアプリケーションが中断されないという事実を非常に簡単に無視することができます。インタフェースで定義されたクラスは、それを実装したクラスには存在しません。私のアプリは、フィードからデータを取得するために使用するクラスを気にする必要はありません。そのデータを取得するために必要なメソッドだけが存在するということです。

これをさらに一歩進めると、別のフィードタイプを追加する目的でカレンダーアプリケーションに戻ったときに、このインターフェイスが非常に役立つことがわかります。 ImportableFeedインターフェースを使用するということは、このインターフェースを実装する新しいクラスを追加するだけで、さまざまなフィードタイプをインポートするクラスをさらに追加し続けることができるということです。私のコアアプリケーションは、私の新しいフィードインポートクラスがImportableFeedインターフェースを実装する限り、インターフェースが必要とする利用可能なパブリックメソッドがあることだけに頼っているので、これによって私はコアアプリケーションに不必要に大量を追加せずにたくさんの機能を追加できます。私はそれを定位置に落として動かし続けることができることを知っています。

これは非常に単純なスタートです。その後、私のすべてのカレンダークラスが実装するために必要となる、クラスが処理するフィードタイプに固有のより多くの機能を提供する別のインターフェースを作成できます。もう1つの良い例は、フィードの種類などを確認する方法です。

これは問題を超えていますが、上の例を使用したので。 この方法で使用すると、インタフェースには独自の問題があります。実装したメソッドから返される出力を、インターフェイスと一致するようにし、そのためにPHPDocブロックを読み込むIDEを使用して、戻り値の型をインターフェイスのPHPDocブロックの型ヒントとして追加する必要があります。それを実装する具象クラスに変換します。このインタフェースを実装するクラスからのデータ出力を消費する私のクラスは、少なくともこの例で返される配列を期待していることを知っているでしょう:

<?php
interface ImportableFeed 
{
    /**
     * @return array
     */
    public function getEvents();
}

抽象クラスとインタフェースを比較する余地はあまりありません。インタフェースは、実装されたときにクラスが一連のパブリックインタフェースを持つことを要求する単なるマップです。


  • 良い説明:)ありがとう! - Razvan.432
  • 情報を追加するためだけに:抽象クラスで、メソッドを抽象として宣言した場合、それはインターフェースのように動作するので、getEvents()をオーバーライドしなかったという事実を無視することはできません。アプリはインターフェースと同じように失敗します。 - Rikudou_Sennin

5

私の意見では、インタフェースは非機能抽象クラスよりも優先されるべきです。 2つを解析して組み合わせるのではなく、インスタンス化されたオブジェクトが1つだけなので、パフォーマンスがそこでさえ打たれても驚くには当たらないでしょう(ただし、確信は持てませんが、内部の動作には慣れていません)。 OOP PHPの)。

インターフェースは、たとえばJavaと比べて有用性や意味が少ないというのは事実です。一方、PHP 6では、戻り値の型ヒントなど、さらに多くの型ヒントが導入される予定です。これはPHPインターフェースに何らかの価値を追加するはずです。

tl; dr:interfacesは従う必要があるメソッドのリストを定義しています(APIと考えてください)が、抽象クラスは基本的または共通の機能を提供し、サブクラスはそれを特定のニーズに合わせて絞り込みます。


  • PHP 6は決してリリースされません。 PHP 6は2005年から2010年にかけて開発中のプロジェクトでしたが、延期され、最終的にキャンセルされました。 PHP 7が次のバージョンです。これは主に、以前のPHP 6プロジェクトとの混同を避けるためです。 - Ashu Jha

5

インターフェイスは、開発者が特定のメソッドを確実に実装するためのものではありません。これらのクラスは特定のメソッドを持つことが保証されているため、クラスの実際の型がわからなくてもこれらのメソッドを使用できます。例:

interface Readable {
  String read();
}

List<Readable> readables; // dunno what these actually are, but we know they have read();
for(Readable reader : readables)
  System.out.println(reader.read());

多くの場合、抽象クラスであろうとなかろうと、基本クラスを提供することは意味がありません。実装は大きく異なり、いくつかのメソッド以外は共通のものを共有していないためです。

動的に型付けされた言語は、あなたがインターフェースを必要としないところでは「ダックタイピング」の概念を持ちます。あなたは、そのオブジェクトがあなたがそれに呼んでいるメソッドを持っていると仮定するのは自由です。これは静的に型付けされた言語で、あなたのオブジェクトが何らかの方法(私の例ではread())を持っているが、インターフェースを実装していないという問題を回避します。


4

PHPがこの点で異なるのかどうか私は覚えていませんが、Javaでは、複数のインターフェースを実装できますが、複数の抽象クラスを継承することはできません。 PHPも同じように機能すると思います。

PHPでは、コンマで区切って複数のインターフェースを適用することができます(私は、それがきれいな解決策ではないと思います)。

複数の抽象クラスに関しては、お互いを拡張する複数の抄録を持つことができます(これについても、私は完全には確信が持てませんが、以前にどこかで見たことがあると思います)。あなたが拡張できない唯一のものは最終クラスです。


3

インターフェースはあなたのコードにパフォーマンスの向上やそのような何かを与えることはありませんが、それらはそれを保守可能にすることに向けて長い道のりを行くことができます。コードへのインターフェースを確立するために抽象クラス(あるいは非抽象クラスでさえも)を使用することができますが、適切なインターフェース(キーワードで定義し、メソッドシグネチャのみを含むもの)を使用するのは簡単です。整理して読んでください。

そうは言っても、クラスを介してインターフェースを使用するかどうかを決定するときは、慎重に判断するのが一般的です。デフォルトのメソッド実装、またはすべてのサブクラスに共通の変数が必要な場合があります。

もちろん、マルチインターフェースの実装についてのポイントもまた健全です。複数のインタフェースを実装するクラスがある場合は、そのクラスのオブジェクトを同じアプリケーション内の異なる型として使用できます。

しかし、あなたの質問がPHPに関するものであるという事実は、物事をもう少し面白くします。インターフェースにタイプすることは、そのタイプに関係なく、あなたがほとんどすべてのメソッドに何かを供給することができるPHPではまだ信じられないほど必要ではありません。メソッドパラメータを静的に入力することはできますが、そのいくつかは壊れています(String、私は信じていますが、いくつかの問題が発生します)。他のほとんどの参照を入力することはできず、PHPで静的な入力を強制しようとしてもあまり意味がありません。この時点で)そしてそれが理由で、インターフェースの価値PHPでこの時点でより強く型付けされた言語よりもはるかに少ないです。読みやすさという利点がありますが、それ以外はほとんどありません。あなたはまだメソッドを宣言し、それらをインプリメンター内にそれらの本体を与えなければならないので、多重実装はさらに有益ではありません。


1

以下はPHPインターフェースのポイントです。

  1. これは、クラスに必要なメソッドを定義するために使用されます[htmlをロードする場合はidとnameが必要です。この場合、インターフェイスにはsetIDとsetNameが含まれます]。
  2. インターフェイスはクラスにその中で定義されているすべてのメソッドを含めるように厳密に強制します。
  3. パブリックアクセシビリティとのインターフェースでのみメソッドを定義できます。
  4. クラスのようにインターフェースを拡張することもできます。 extendsキーワードを使ってphpのインターフェースを拡張できます。
  5. 複数のインターフェースを拡張する
  6. 両方が同じ名前の機能を共有する場合は、2つのインターフェースを実装することはできません。エラーが発生します。

コード例:

interface test{
    public function A($i);
    public function B($j = 20);
}

class xyz implements test{
    public function A($a){
        echo "CLASS A Value is ".$a;
    }
    public function B($b){
        echo "CLASS B Value is ".$b;
    }
}
$x = new xyz();
echo $x->A(11);
echo "<br/>";
echo $x->B(10);


1

抽象クラスとインタフェースは、子クラスに実装する必要がある抽象メソッドを提供するという点で類似しています。ただし、それらにはまだ次のような違いがあります。

1.インタフェースには抽象メソッドと定数を含めることができますが、具体的なメソッドと変数を含めることはできません。

2.インタフェース内のすべてのメソッドは、パブリック視認性   範囲。

それは継承することができます3.Aクラスは、複数のインタフェースを実装することができます   1つの抽象クラスからのみ

                                  interface                      abstract class
the code                     - abstract methods               - abstract methods
                             - constants                      - constants                  
                                                              - concrete methods
                                                              - concrete variables

access modifiers             
                             - public                         - public
                                                              - protected
                                                              - private
                                                                etc.
number of parents          The same class can implement
                           more than 1 interface              The child class can 
                                                              inherit only from 1 abstract class

これが誰にとっても理解に役立つことを願っています!


0

インターフェースはあなたの遺伝子のようなものです。

抽象クラスはあなたの実際の両親のようなものです。

それらの目的は遺伝的ですが、抽象クラスとインタフェースの場合、継承されるものはより具体的です。

関連する質問

最近の質問