読者です 読者をやめる 読者になる 読者になる

PHPにはインターフェイスというものがありますよ、という話

なにかとPerlPythonRubyJavaScript等の言語と比べて機能の不足を言及されることの多いPHPですが、
PHPには逆にこれらの言語にはない機能がある、それはインターフェイスだ、という話です。
このインターフェイスという奴は、タイプヒンティングと共に、PHPという言語がさっき挙げたような他の軽量言語とは違ったプログラミングスタイルを持っていることを象徴しています。

インターフェイスって何?

JavaやAS3なんかをやっている人だとわかるかと思いますが、有体にいえばinterfaceとはメソッドの宣言を集めたものです。
PHPマニュアルから説明を引用すると

インターフェイスにより、あるクラスが実装する必要があるメソッドの 種類を、
これらのメソッドの実体を定義することなく、指定するコードを作成できるようになります。
(PHP: オブジェクト インターフェイス - Manual)

コードで言うとこういうやつです。

<?php
interface Car
{
    public function run();
    public function stop();
}

インターフェイスはimplements演算子で実装できます。
Carインターフェイスを実装したMiraクラスは以下のようになります。

<?php
class Mira implements Car
{
    public function run()
    {
        // 何らかの処理
    }
    public function stop()
    {
        // 何らかの処理
    }
}

implementsしたインターフェイスメソッド宣言をきちんと真似しないと、PHPがエラーを出してくれます。

これ何が嬉しいの?

はじめて見た人にとっては多分意味がわからないと思います。
僕は初めてJavaインターフェイスに出会ったときこういう疑問を抱きました。

  • 「なぜわざわざ同じメソッド宣言を繰り返す必要が?」
  • 「機能を実装しているクラスだけでいいのでは?」


これは全くその通りです。
事実、オブジェクト指向においてインターフェイスという機能がなくてもプログラムはいくらでもかけます。
が、インターフェイスを使うことで得られるメリットがあります。


例えばさっきのCarインターフェイスを使うコードの場合。

<?php
// Carインターフェイスを通じて何らかの処理を遂行する関数procedure
function procedure(Car $somecar)
{
    $somecar->run();
    // 何らかの処理
    $somecar->stop();
}

このprocedure関数では引数$somecarの部分でタイプヒンティングしています。
引数として渡された$somecarは、Carインターフェイスを実装したクラスのインスタンスであることが保障されています。


もしprocedure関数に文字列を渡して呼び出した場合、きちんとPHPが以下のようにエラーを出してくれます。

Argument 1 passed to procedure() must implement interface Car, string given, called in ...

インターフェイスを使わずに実装クラスでタイプヒンティングすればいいのでは?

わざわざインターフェイスを使わずとも上記の例ではMiraクラスでタイプヒンティングすればいいではないか、と思う人もいるかもしれません。

<?php
function procedure(Mira $mira)
{
    $mira->run();
    // 何らかの処理
    $mira->stop();
}

これでも良いんじゃないか、ということですが、引数にMiraクラスのインスタンスのみしか受け付けないのならこれでも良いのです。
しかし、これではprocedure関数はMiraクラスの実装に依存する形になってしまいます。
対して、インターフェイスをタイプヒンティングすれば、実装を交換できます。

実装の交換?

<?php
class Copen implements Car
{
    public function run()
    {
        // ...
    }
    public function stop()
    {
        // ...
    }
}

例えば新しくCarインターフェイスを実装したCopenクラスを作ります。
Carインターフェイスでタイプヒンティングするのであれば、このクラスのインスタンスは、procedure関数に渡すことができます。
Miraクラスでタイプヒンティングした場合はCopenクラスのインスタンスを引数に渡せません。
インターフェイスを使うことで特定のクラスの実装に依存しないタイプヒンティングが可能になります。
いわゆる機能(メソッド宣言)と実装の分離というやつです。
これにより型の安全性と柔軟さを同時に得ることができます。

他の軽量言語にインターフェイスがなく、PHPにだけある理由

インターフェイスを使うことは一長一短です。
というのも、インターフェイスを使った場合、コードの記述量は増えるわけで、簡潔な記述とは程遠いものになることがあります。
その代わり、上記のようにインターフェイスをタイプヒンティングすることで、自由にロジックの交換ができる上に、引数は型が保障されます。


PHPインターフェイスがある理由とは、
簡潔な記述よりも、記述のわかりやすさや型の安全性を重視しているPHPの設計思想の表れなわけです。

インターフェイスのより具体的な例を知りたい方は

デザインパターン関連のドキュメントにたくさんあります。
リンクをいくつか挙げておきます。

最後に

なんかここ変じゃね?みたいなところがあったら突っ込みお願いします。

追記

一応言っておくと、ダックタイピングよりもインターフェイス使った方が良いよっていう話をしているわけではなくて、PHPにはこういうスタイルがあるよ、と言ってるだけです。
タイトルそのままのことを言ってます。