web-dev-qa-db-ja.com

インターフェイスまたは抽象クラス:どちらを使用しますか?

PHP interfaceを使用する必要がある場合と、abstract classを使用する必要がある場合を説明してください。

abstract classinterfaceに変更するにはどうすればよいですか?

301
user220758

システム(自分自身を含む)で作業している開発者に、ビルドするクラスに一連のメソッドを実装するように強制する場合は、インターフェイスを使用します。

システム(自分自身を含む)で作業する開発者に、いくつかのベースを提供するメソッドおよびのセット番号を実装するように強制する場合は、抽象クラスを使用します子クラスの開発に役立つメソッド。

留意すべきもう1つの点は、クライアントクラスは1つの抽象クラスしか拡張できないのに対し、複数のインターフェイスを実装できることです。したがって、抽象クラスで動作コントラクトを定義している場合、各子クラスは単一のコントラクトにのみ準拠する可能性があります。特定のパスに沿ってユーザープログラマーを強制する場合、これは良いことです。それ以外の場合は悪いでしょう。 PHPのCountableインターフェースとIteratorインターフェースがインターフェースではなく抽象クラスであったと想像してください。

どの方法を使用するかわからない場合( cletus以下 で述べたように)よくあるアプローチの1つは、インターフェイスを作成し、抽象クラスにそのインターフェイスを実装させることです。

430
Alan Storm

Abstract ClassInterfaceの違い:

抽象クラ​​ス

抽象クラスは、機能を提供するおよび派生クラスの残りを残すを使用できます。

  • 派生クラスオーバーライドする場合としない場合基本クラスで定義された具象関数。

  • 抽象クラスから拡張された子クラスは、論理的に関連している必要があります。

インターフェース

インターフェースは機能を含むことはできません。 Itonlyにはメソッドの定義が含まれます。

  • 派生クラスインターフェイスで定義されたすべてのメソッドのコードを提供する必要があります

  • インターフェイスを使用して、完全に異なる関連のないクラスを論理的にグループ化できます。

151
kn3l

抽象クラスを使用する理由以下は簡単な例です。次のコードがあるとしましょう:

<?php 

class Fruit {
    private $color;

    public function eat() {
        // chew
    }

    public function setColor($c) {
        $this->color = $c;
    }
}

class Apple extends Fruit {
    public function eat() {
        // chew until core
    }
}

class Orange extends Fruit {
    public function eat() {
        // peeling
        // chew
    }
}

今、私はあなたにAppleを与え、あなたはそれを食べる。どんな味?リンゴのような味がします。

<?php 
$Apple = new Apple();
$Apple->eat();

// Now I give you a fruit.
$fruit = new Fruit();
$fruit->eat();

その味はどうですか?まあ、それはあまり意味をなさないので、あなたはそれをすることができないはずです。これは、Fruitクラスを抽象化し、その中にeatメソッドを作成することで実現されます。

<?php 
abstract class Fruit {
    private $color;

    abstract public function eat(){}

    public function setColor($c) {
        $this->color = $c;
    }
}
?>

抽象クラスはインターフェイスに似ていますが、メソッドは抽象クラスで定義できますが、インターフェイスではすべて抽象メソッドです。抽象クラスには、空のメソッドと作業/コンクリートメソッドの両方を含めることができます。インターフェイスでは、そこに定義されている関数にボディを含めることはできません。抽象クラスでは、できます。

実世界の例:

<?php 
abstract class person {

    public $LastName;
    public $FirstName;
    public $BirthDate;

    abstract protected function write_info();
}

final class employee extends person{

    public $EmployeeNumber;
    public $DateHired;

    public function write_info(){
        //sql codes here
        echo "Writing ". $this->LastName . "'s info to emloyee dbase table <br>";   
    }
}

final class student extends person{

    public $StudentNumber;
    public $CourseName;

    public function write_info(){
        //sql codes here
        echo "Writing ". $this->LastName . "'s info to student dbase table <br>";
    }
}

///----------
$personA = new employee;
$personB = new student;

$personA->FirstName="Joe";
$personA->LastName="Sbody";

$personB->FirstName="Ben";
$personB->LastName="Dover";

$personA->write_info();
// Writing Sbody's info to emloyee dbase table
$personB->write_info();
// Writing Dover's info to student dbase table 
117

ベストプラクティスは、インターフェイスを使用してコントラクトと抽象クラスをその実装の1つとして指定することです。その抽象クラスは定型文の多くを埋めることができるため、特定の実装を強制的に使用せずに、必要なものまたは必要なものをオーバーライドするだけで実装を作成できます。

62
cletus

これをミックスに放り込むためだけですが、Cletusが抽象クラスと組み合わせてインターフェイスを使用することについて言及したように、私はインターフェイスを使用してデザイン思考を明確にします。

例えば:

<?php
class parser implements parserDecoratorPattern {
    //...
}

そうすれば、私のコードを読んでいる人(そしてデコレーターパターンが何であるかを知っている人)は、a)パーサーをどのように構築し、b)デコレーターパターンを実装するためにどのメソッドが使用されているかをすぐに知ることができます。

また、私はJava/C++/etcプログラマーではないので、ここではベースから外れているかもしれませんが、データ型はここで使用できます。オブジェクトは型であり、型を渡すときはプログラム的に重要です。コントラクト可能なアイテムをインターフェイスに移動すると、メソッドが返す型のみが決まり、それを実装するクラスの基本型は決まりません。

遅れており、より良い擬似コードの例を考えることはできませんが、ここに行きます:

<?php
interface TelevisionControls {};
class Remote implements TelevisionControls {};
class Spouse implements TelevisionControls {};
Spouse spouse = new Spouse();
Remote remote = new Remote();
isSameType = (bool)(remote == spouse)
37
Austen Hoogen

主な違いは、抽象クラスにはデフォルトの実装を含めることができますが、インターフェースにはできないことです。

インターフェースは、実装のない動作のコントラクトです。

13
Mitch Wheat

また、ここに追加したいのは、他のOO言語が何らかの種類のインターフェースと抽象化を持っているからといって、PHPと同じ意味と目的があるという意味ではありません。抽象化/インターフェースの使用は少し異なりますが、PHPのインターフェースには実際の機能はありません。それらは、セマンティックおよびスキーム関連の理由でのみ使用されます。重要なのは、開発者の使用計画がまったく異なるかどうかに関係なく、プロジェクトをできるだけ柔軟で、将来の拡張機能に対して拡張可能かつ安全にすることです。

英語が母国語でない場合は、抽象化とインターフェースが実際に何であるかを調べることができます。また、同義語も探します。

そして、これは比phorとしてあなたを助けるかもしれません:

INTERFACE

たとえば、イチゴで新しい種類のケーキを焼き、材料と手順を説明するレシピを作成したとします。なぜそれがとても美味しく、ゲストがそれを好むのかを知っているのはあなただけです。次に、他の人もそのケーキを試すことができるように、レシピを公開することにします。

ここのポイントは

-正しくするために
-注意する
-悪くなる可能性のあるもの(イチゴが多すぎるなど)を防ぐため
-試してみる人が簡単に使えるように
-何をすればいいのか(攪拌など)
-あなたはできるが、持っていないことを伝える

まさにこれがインターフェースを記述するものです。これはガイドであり、レシピの内容を観察する一連の指示です。 PHPでプロジェクトを作成し、GitHubで、または仲間などでコードを提供する場合と同じです。インターフェイスとは、人々ができること、すべきでないことです。それを保持するルール-あなたがそれに従わないと、構造全体が壊れます。


抽象化

ここでこの比phorを続けるために...想像してみてください、あなたは今度はそのケーキを食べているゲストです。そして、あなたは今、レシピを使ってそのケーキを試しています。ただし、新しい材料を追加したり、レシピに記載されている手順を変更/スキップしたい場合があります。それでは、次に何が来るのでしょうか?そのケーキの別のバージョンを計画します。今回は、ストローベリーではなくブラックベリーを使用し、バニラクリームを追加しました。

これは、元のケーキの拡張と考えることができるものです。基本的には、新しいレシピを作成することで抽象化を行います。これは、まったく異なるためです。いくつかの新しい手順とその他の要素があります。ただし、ブラックベリーバージョンには、オリジナルから引き継いだいくつかの部分があります。これらは、あらゆる種類のケーキに必要な基本手順です。まるで牛乳のような材料のように-それはすべての派生クラスが持っているものです。

材料と手順を交換したいので、それらをそのケーキの新しいバージョンで定義する必要があります。これらはabstract methodsです。これは新しいケーキに対して定義する必要があります。なぜなら、ケーキには果物があるはずなのにどれですか?それで、今回は黒い実を取ります。できた.

そこに行くと、ケーキを拡張し、インターフェイスに従い、そこからステップと材料を抽象化しました。

12
Thielicious

物理学の観点から:

  • 抽象クラスは、「is a」関係を表します。果物があるとしましょう。よくある責任と共通の行動を共有するFruit抽象クラスがあります。

  • インターフェイスは、「すべき」関係を表します。私の意見では、インターフェイス(ジュニア開発者の意見)は、アクション、またはアクションに近いもので命名する必要があります(申し訳ありませんが、Wordが見つかりません。私は英語のネイティブスピーカーではありません) IEatableと言いましょう。食べられることは知っていますが、何を食べているのかわかりません。

コーディングの観点から:

  • オブジェクトにコードが重複している場合、それらが共通の動作をしていることを示しています。つまり、インターフェイスでは実行できないコードを再利用するために抽象クラスが必要になる場合があります。

  • もう1つの違いは、オブジェクトは必要な数のインターフェイスを実装できますが、「ダイヤモンドの問題」のため抽象クラスは1つしか持てないということです(理由についてはこちらをご覧ください! http://en.wikipedia。 org/wiki/Multiple_inheritance#The_diamond_problem

私はおそらくいくつかの点を忘れますが、物事を明確にすることができると思います。

PS: "is a"/"should do"はVivek Vermaniの答えによってもたらされました。私は彼の答えを盗むつもりはありませんでした。

10
IEatBagels

すでに優れた答えをいくつか追加するには:

  • 抽象クラスを使用すると、ある程度の実装を提供できます。インターフェイスは純粋なテンプレートです。インターフェイスは機能のみを定義でき、実装することはできません。

  • インターフェイスを実装するクラスは、定義するすべてのメソッドの実装をコミットするか、抽象として宣言する必要があります。

  • インターフェイスは、Javaと同様に、PHPが多重継承をサポートしないという事実を管理するのに役立ちます。 PHPクラスは、単一の親のみを拡張できます。ただし、必要な数のインターフェイスを実装することをクラスに約束させることができます。

  • type:実装するインターフェイスごとに、クラスは対応するタイプを取ります。任意のクラスがインターフェイス(または複数のインターフェイス)を実装できるため、インターフェイスは他の方法では無関係なタイプを効果的に結合します。

  • クラスはスーパークラスを拡張し、任意の数のインターフェイスを実装できます。

    class SubClass extends ParentClass implements Interface1, Interface2 {
        // ...
    }
    

インターフェイスをいつ使用し、抽象クラスをいつ使用する必要があるか説明してください。

実装をまったく行わないテンプレートのみを提供する必要がある場合にインターフェイスを使用し、そのインターフェイスを実装するクラスに、少なくともそれを実装する他のクラスと同じメソッドがあることを確認します。

他のオブジェクトの基盤(部分的に構築されたクラス)を作成する場合は、抽象クラスを使用します。抽象クラスを拡張するクラスは、定義または実装されたいくつかのプロパティまたはメソッドを使用します。

<?php
// interface
class X implements Y { } // this is saying that "X" agrees to speak language "Y" with your code.

// abstract class
class X extends Y { } // this is saying that "X" is going to complete the partial class "Y".
?>

抽象クラスをインターフェイスに変更するにはどうすればよいですか?

簡単なケース/例を次に示します。実装の詳細を取り出します。たとえば、抽象クラスを次から変更します。

abstract class ClassToBuildUpon {
    public function doSomething() {
          echo 'Did something.';
    }
}

に:

interface ClassToBuildUpon {
    public function doSomething();
}
10
bg17aw

抽象クラスとインターフェースの技術的な違いは、他の回答に正確にリストされています。オブジェクト指向プログラミングのためにコードを書いている間に、クラスとインターフェースを選択するための説明を追加したいと思います。

クラスはエンティティを表し、インターフェイスは動作を表す必要があります。

例を見てみましょう。コンピューターモニターはエンティティであり、クラスとして表す必要があります。

class Monitor{
    private int monitorNo;
}

表示インターフェースを提供するように設計されているため、機能はインターフェースによって定義する必要があります。

interface Display{
    void display();
}

他の回答で説明されているように考慮すべき他の多くのことがありますが、これはコーディング中にほとんどの人が無視する最も基本的なものです。

6
rags147

両方を使用する必要がある場合の例を追加したいだけです。現在、汎用ERPソリューションでデータベースモデルにバインドされたファイルハンドラを作成しています。

  • 標準のクラッドを処理する複数の抽象クラスと、さまざまなカテゴリのファイルの変換やストリーミングなどの特殊な機能もあります。
  • ファイルアクセスインターフェイスは、ファイルの取得、保存、および削除に必要なメソッドの共通セットを定義します。

このようにして、異なるファイル用の複数のテンプレートと、明確な区別のあるインターフェイスメソッドの共通セットを使用できます。このインターフェイスは、基本抽象クラスの場合ではなく、アクセスメソッドに正しい類似性を提供します。

さらに別のファイルストレージサービス用のアダプタを作成する場合、この実装により、インターフェイスをまったく異なるコンテキストで他の場所で使用できるようになります。

1
Umair Ahmed