PHPにはインターフェイス(interface)という機能があります。存在自体は広く知られていますので、ご存じな方も多いことでしょう。
しかしながら、今一よく分からない。なんとなく分かっている(つもり)。文法は分かるけど使い所が分からない……。という方も多いのではないでしょうか。
はい、フリーランス歴20年以上の私も似たようなものです!
私も文法はハッキリと知ってます。使い所も(教科書的に)おおよそ理解しているつもりです。……でも実務で使うとなると、なかなか使うタイミングが無い。。
もしかしたら理解が足りていないのかもしれないと思い、インターフェイスの入門記事を書くことにしました(※1)。発端は自分の為ですが、クラスを理解した後程度の初心者の方や、同じような状況にある方の参考になれば幸いです。
インターフェイスとは!?
インターフェイス(=interface)という英単語は、「界面」や「接点」といった意味を持つ単語です。
PHPではクラスに近い存在として定義可能です。
以下がインターフェースのコード例ですので、初めての方はまず雰囲気だけ感じ取ってください。
<?php
// インターフェイスを宣言してるよ
interface SampleInterface {
// 中身が無いメソッドを定義できるよ
public function sampleMethod();
}
ここではまだ具体的なことは分からなくてOKです!
クラスは単体で使えますが、インターフェイスは単体で使えません。また、クラスは「継承」して使えますが、インターフェイスはクラスから「実装」して使います。
読みについて
私はPHP以外の言語から入り、複数言語の経験がありますが、一般的にインターフェースと記載してある文献が多かったように思います(たまたまかもしれません)。
しかしPHPの公式マニュアルを読むとインターフェイスと記載がありました。
本ページでは公式マニュアルに従っての表記にしますが、どれでも通じますし、ダメということではないです。
何が便利なの!?
具体的なメリットは、インターフェイスで定義した中身の無いメソッドを、実装したクラス内で強制的に定義させることができる点です。
例えば以下では、Sample
クラスにてSampleInterface
を実装しています。
// インターフェイスを宣言してるよ
interface SampleInterface {
// 中身が無いメソッドを定義できるよ
public function sampleMethod();
}
// インターフェイスを実装した空のクラス
class Sample implements SampleInterface{
}
上記コードはSample
クラスでsampleMethod()
を実装していません。そのためエラーになってしまいます。
以下の様にしてやれば、エラーが解消されます。
// インターフェイスを宣言してるよ
interface SampleInterface {
// 中身が無いメソッドを定義できるよ
public function sampleMethod();
}
// インターフェイスを実装したクラス
class Sample implements SampleInterface{
public function sampleMethod() {
echo 'Hello!';
}
}
このように、あるクラスが特定のメソッドを持つことを「強制」し、実装を「保証」することができる。ひいては複数のクラスに共通のメソッドを実装させる際に便利なのです。
インターフェイスの基本
機能やメリットがが分かったところで、改めて、構文などの基本的な部分にふれていきます。
インターフェイスの基本構文
interface インターフェース名 {
public function メソッド名();
}
宣言はクラス・トレイト・列挙型などと同様ですね。
最も特徴的なのは、メソッドの中身が無い(メソッドのベースだけ)ということです。すでに述べたとおり、中身はこのインターフェースを実装したクラスで実装しなければなりません。
また、必ずpublic
なメソッドである必要があります。
簡単な使用例
以下は最低限の簡単な使用例です。
<?php
// インターフェイスの定義
interface CarInterface {
public function drive();
}
// インターフェイスを実装するクラス
class Toyota implements CarInterface {
// deive()メソッドを定義しないとエラーになる
public function drive() {
echo "トヨタ車が走ります。";
}
}
$toyota = new Toyota();
// トヨタ車が走ります。
$toyota->drive();
8行目のimplements
でCarInterface
を実装しています。
そして10-12行目でdrive()
メソッドの具体的な中身を定義しています。これを忘れるとエラーになるのですぐわかります。
後は普通のクラスを理解していれば、特に難しいことはありませんね。
できないこと
基本が分かったところで、できないことを知っておくと理解が深まります。それぞれ見ていきましょう。
メソッドの具体的な実装
インターフェイス内で具体的にメソッドの内容を実装することはできません。Fatal errorが発生します。
<?php
// PHP Fatal error発生
interface CarInterface {
public function drive(){
echo "走ります";
}
}
プロパティの宣言
プロパティを宣言することはできません。同じくFatal errorが発生します。
<?php
// PHP Fatal error発生
interface CarInterface {
public $drive;
}
コンストラクタの実装
予期せぬ結果を生む可能性があるため、コンストラクタの実装はできないもの(後述)と考えた方が良いでしょう。
単体でnewすること
具体的なメソッドの内容が実装できないのでいわずもがなですが、インターフェイスを単体でnew
することはできません。
すでに述べた通り、クラスで実装して使います。
public以外の指定
メソッドをpublic
修飾子以外に指定することはできません。protected
やprivate
を指定すると、Fatal errorが発生します。
<?php
// PHP Fatal error発生
interface CarInterface {
protected function drive();
private function stop();
}
finalメソッドの定義
普通に考えればあたりまえですが、final
修飾子を付けるとFatal errorが発生します。
<?php
// PHP Fatal error発生
interface CarInterface {
public final function drive();
}
使い方応用編
覚えておかなくても使えますが、こんなこともできる……という機能をご紹介します。
多重実装ができる
インターフェースの特徴として、多重実装が可能です。
<?php
// インターフェイスの定義
interface Walkable {
public function walk();
}
interface Runnable {
public function run();
}
// 複数のインターフェイスを実装するクラス
class Human implements Walkable, Runnable {
public function walk() {
echo "人は歩くことができます。\n";
}
public function run() {
echo "人は走ることができます。\n";
}
}
// インスタンスの作成とメソッドの呼び出し
$person = new Human();
// 出力: 人は歩くことができます。
$person->walk();
// 出力: 人は走ることができます。
$person->run();
PHPの継承に関しては、単一継承なので時に不便を感じることもあるかもしれません。インターフェイスを使えば、多重継承のようなことが実現できます。
静的メソッドを定義できる
本来の用途にあてはまるかは分かりませんが、静的メソッドも定義可能です。
<?php
// インターフェイスの定義
interface Walkable {
static public function walk();
}
interface Runnable {
static public function run();
}
// 複数のインターフェイスを実装するクラス
class Human implements Walkable, Runnable {
static public function walk() {
echo "人は歩くことができます。\n";
}
static public function run() {
echo "人は走ることができます。\n";
}
}
// インスタンスの作成とメソッドの呼び出し
// 出力: 人は歩くことができます。
Human::walk();
// 出力: 人は走ることができます。
Human::run();
個人的には一度も使ったことはなく、今回マニュアルを読んで初めて知りました。
マジックメソッドを定義できる
クラスと同様、マジックメソッドを定義することも可能です。
<?php
// インターフェイスでマジックメソッドを定義
interface Stringable {
public function __toString();
}
// インターフェイスを実装するクラス
class User implements Stringable {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function __toString() {
return "User: " . $this->name;
}
}
// インスタンスの作成とマジックメソッドの使用
$user = new User("Alice");
echo $user; // __toStringメソッドが自動的に呼び出される
定数を定義できる
クラスと同様、定数を定義することができます。
インターフェイス内で定義された定数は、そのインターフェイスを実装するすべてのクラスで使用できます。
<?php
// インターフェイスで定数を定義
interface Configurable {
const MAX_SPEED = 300;
const MIN_SPEED = 0;
}
// インターフェイスを実装するクラス
class Car implements Configurable {
public function getMaxSpeed() {
return Configurable::MAX_SPEED;
}
public function getMinSpeed() {
return self::MIN_SPEED; // クラス内での定数の参照方法
}
}
// インスタンスの作成とメソッドの呼び出し
$car = new Car();
echo "最大速度: " . $car->getMaxSpeed() . " km/h\n";
echo "最小速度: " . $car->getMinSpeed() . " km/h\n";
継承ができる
インターフェイス同士で継承ができます。
<?php
// インターフェイスの定義
interface Movable {
public function move();
}
// Movableインターフェイスを継承するインターフェイス
interface Walkable extends Movable {
public function walk();
}
interface Runnable extends Movable {
public function run();
}
// 複数のインターフェイスを実装するクラス
class Human implements Walkable, Runnable {
public function move() {
echo "人は移動します。\n";
}
public function walk() {
echo "人は歩きます。\n";
}
public function run() {
echo "人は走ります。\n";
}
}
// インスタンスの作成とメソッドの呼び出し
$person = new Human();
$person->move();
$person->walk();
$person->run();
上記のようにしたRunnable
インターフェースは、walk()
&move()
メソッドも呼び出せます。
ただ、意味のある継承にするために「is-a」の関係(後述)にする必要があると考えられます。個人的には、多重実装の方が適しているシーンが多そうに思います。
クラスとの使い分けが難しくなってきますね。少なくとも私には使い所がこれまでありませんでした。
クラスとインターフェイスの使い分け
クラスでも似たようなことが出来る!?どう使い分ければ良い!?
このように思う方が多いと思います。改めてどう使い分けたら良いのか考えてみました。
クラスはis-aの関係
クラスはis-aの関係を表す際に使います。
たとえば、「犬は動物である」(Dog is an Animal)という関係です。この場合、Dog
クラスはAnimal
クラスを継承することで、Animal
のすべての特性(プロパティとメソッド)を引き継ぎます。
ですから、is-aの関係であれば通常はクラスを使えば良いです。
インターフェイスはcan-doの関係
対してインターフェイスは「can-do」関係(または「has-a」関係)、つまりオブジェクトが特定の行動や能力を持つ(実装する)ことを表します。
たとえば、「飛行可能(Flyable)」インターフェイスは、それを実装するどのクラスも飛行する能力を持てることを意味します。これはis-a関係とは異なり、クラスが特定の行動をできる(can-do)ことを示しています。
例えば「飛行機クラス(Plane)」があり、「飛行可能(Flyable)」インターフェイスを実装するのは自然です。
それでもよくわからない……という方へ
関係を考えることで、イメージできることが多いと思います。
しかし、実際にはis-aともcan-doとも取れるようなシーンもあるかもしれません。
ですので、
文法的には分かったけど、なんとなく分かったようなわからないような……
このような方は多いと思います。
実際問題として、独自のフレームワークやライブラリを作ったり、デザインパターンを駆使するような段階にならないと、あまりインターフェースのメリット・使い所は分からないのかな、と思います。
ですのでもっと理解を深めたい方は、デザインパターンと併せて学ぶのが良いかもしれませんね。
デザインパターンは、プログラム設計の手法・テクニックです。「定石」と言えば分かりやすいでしょうか。詳しくは以下の記事もご覧ください。
本ページで基本を学んだ後は、是非デザインパターンと併せて学んでみてください。
まとめ
以上、インターフェイスについて初心者向けに解説しました。
インターフェイスは、初心者にとって比較難しい概念だと思います。しかしながら、ひとまずPHPで飯を食っていくためには、最低限インターフェースの意味と文法を知っておけばOKです。
自分で使いこなせなくとも、使いこなせる人が書いたライブラリ・フレームワークを利用すれば、大抵の案件はこなせます。
実際、私もPHP3から書いてますが、深くまで理解しているとは言えませんので(汗)
独自のライブラリを作ったり、デザインパターンを使いこなす段階で、改めて学び直しても良いかもしれませんね。
コメント