web-dev-qa-db-ja.com

PHPにインターフェースがあるのはなぜですか?

PHP5以降、言語にインターフェースが追加されていることに気付きました。しかし、PHPは型が緩いので、インターフェースを使用する利点のほとんどが失われているようです。なぜこれが言語に含まれているのですか?

34
GSto

PHP=のインターフェースの主な利点は、クラスが複数のインターフェースを実装できることです。これにより、一部の機能を共有するが、親クラスを必ずしも共有しないクラスをグループ化できます。いくつかの例には、キャッシュ、出力が含まれる場合があります、または特定の方法でクラスのプロパティにアクセスします。

コードでは、クラス名をチェックする代わりに、クラスが特定のインターフェースを実装しているかどうかをチェックできます。その後、新しいクラスが追加されてもコードは機能します。

PHPは、さまざまな状況で役立つ可能性があるいくつかの事前定義されたインターフェースを提供します: http://php.net/manual/en/reserved.interfaces.php

編集-例の追加

MyInterfaceという名前のインターフェイスがあり、いくつかの機能を共有する場合と共有しない場合のある異なるクラスの複数のオブジェクトを使用している場合、インターフェイスを使用すると、次のようなことができます。

// Assume $objects is an array of instances of various classes
foreach($objects as $obj) {
 if($obj instanceof MyInterface) {
     $obj->a();
     $obj->b();
     $obj->c();
   }
}
31
pjskeptic

PHPは緩やかに型付けされていますが、メソッドパラメータなどについては強く型付けできます。

次の例について考えてみます。

interface Car { function go(); }

class Porsche { function go() {} }

function drive(Car $car) {}

$porsche = new Porsche();

drive($porsche);

上記のコードは出力します:

Drive()に渡される引数1は、指定されたポルシェのインスタンスであるCarインターフェースを実装する必要があります。

23
Emanuil Rusev

インターフェイスを使用すると、オープン/クローズの原則を実装し、疎結合のコードベースを維持し、最高のOOP設計パターンの多くを実装できます。

たとえば、あるクラスが別のクラスを引数として受け入れる場合:

class A {

    public function __construct(B $class_b) {
        // use class b
        $class_b->run();
    }
}

これでクラスAとクラスBは密結合になり、クラスAはBとそのサブクラス以外の他のクラスを使用できなくなります。タイプヒントは、正しいタイプの引数があることを確認しますが、AとBの関係を固めました。

ただし、クラスAが、run()メソッドを持つすべてのタイプのクラスを使用できるようにするとします。これは基本的に(完全ではありませんが)COMMANDデザインパターンです。解決するには、代わりに具象クラスの代わりにインターフェースを使用してヒントを入力します。次に、Bはそのインターフェースを実装し、クラスAの引数として受け入れられます。これにより、クラスAは、そのインターフェースをコンストラクターの引数として使用する任意のクラスを受け入れることができます。

このタイプのコーディングは、ほとんどのOOP設計パターンで使用されており、後でコードをより簡単に変更できるようにします。これらは、AGILEプログラミングの基本の一部です。

class A {

    public function __construct(C $interface_c) {
        // use any class that implements interface C
        $interface_c->run();
    }
}

interface C {

    public function run();
}

class B implements C {

    public function run() {
        // do something
    }
}
8
dqhendricks

@pjskepticには 良い答え があり、@ KamilTomšíkはその答えについて良いコメントがあります。

PHPのような動的に型付けされた言語の優れた点は、オブジェクトでメソッドを使用しようとすることができ、メソッドがそこにない限り、それがあなたに叫ぶことはないということです。

PHPのような動的に型付けされた言語の問題は、オブジェクトに対してメソッドを使用しようとすることができ、メソッドがそこにないときにそれがあなたに悲鳴を上げることです。

インターフェースは、未知のオブジェクトのメソッドを呼び出す便利な方法を追加し、メソッドがそこにあることを確認します(それらが必ずしも正しいまたは機能するわけではありません)。これは言語の必須部分ではありませんが、コーディングをより便利にします。強く型付けされたOOP開発者が強く型付けされたPHPコードを書くことができるようになり、緩く型付けされたPHP別のPHP開発者。

次のような関数:

foo( IBar $bar )
{
  $baz = $bar->baz();
  ...
}

より便利です:

foo( $bar )
{
  if ( method_exists( $bar, 'baz' ) )
  {
    $baz = $bar->baz();
  }
  else
  {
    throw new Exception('OMGWTF NO BAZ IN BAR!');
  }
  ...
}

そして私見シンプルで読みやすいコードはより良いコードです。

7
zzzzBov

ダックタイパーの場合、それらはまったく役に立ちません。実際、ダックタイピングを行う場合、タイプヒントを使用するライブラリ/フレームワークを操作するのはかなり面倒です。

これは、あらゆる種類の動的メタプログラミング(魔法の方法)にも当てはまります。

5
Kamil Tomšík

PHPは緩やかまたは強くではありませんが、 動的に型付けされます です。

インターフェースについて、最初に自問すべきことは、インターフェースの利点のほとんどは何ですか?

OOPでは、インターフェースはタイプだけでなく、動作も重要です。

PHPにも タイプヒント機能 があるため、Javaなどの純粋なoo言語と同じようにインターフェイスを使用できます。

interface File
{
    public function getLines();
}

CSVFile implements File
{
    public function getLines()
    {}
}

XMLFile implements File 
{
    public function getLines()
    {}
}

JSONFile implements File 
{
    public function getLines()
    {}
}

class FileReader
{
    public function read(File $file)
    {
        foreach($file->getLines() as $line)
        {
            // do something
        }
    }
}

PHPインターフェースの実装では、 PHPUnit を使用して抽象クラスのモックを作成することもできます。これは機能の地獄です。

public function testSomething()
{
    $mock = $this->getMockForAbstractClass('File');

    $mock->expects($this->once())
         ->method('getLines')
         ->will($this->returnValue(array()));

    // do your assertions
}

したがって、基本的には、言語機能を使用することで [〜#〜] solid [〜#〜] 互換アプリケーションをPHPに含めることができます) 。

3
Daniel Ribeiro

インターフェースは、具象よりも依存性注入に役立ちます。ベアボーンの例として:

interface Istore { 
  public function save(); 
}

class Article_DB implements Istore 
{ 
  public function save($data) 
  {
    // do save to format needed.
  } 
}

class Article
{
   private $content;

   public function content($content)
   {
     $this->content = $content;
   }

   public function save(Istore $store)
   {
     $store->save($this->content);
   }
}

$article = new Article();
$article->content('Some content');

$store = new Article_DB();
$article->save($store);

ここで、ニーズが変化し、PDFに保存する場合は、 Articleクラスを汚染する代わりに、その目的のために新しいクラスを作成することができます。

class Article_PDF implements Istore 
{ 
  public function save($data) 
  {
    // do save to format needed.
  } 
}


$article = new Article();
$article->content('Some content');

$store = new Article_PDF();
$article->save($store);

Articleクラスには、保存に使用するクラスがIstoreインターフェイスを実装する必要があるという規約があります。保存場所や保存方法は関係ありません。

0
Pete