RitoLabo

ポリモーフィズムに簡単入門してみれば世界平和の音がする。PHPオブジェクト指向プログラミング

  • 公開:
  • カテゴリ: PHP OOP
  • タグ: PHP,OOP,Polymorphism

オブジェクト指向プログラミングって、通称OOP(object-oriented programming)っていうの、ご存知でしたか?

今回はPHPのコードで、オブジェクト指向においての「ポリモーフィズム」について見ていきます。

アジェンダ
  1. 開発環境
  2. オブジェクト指向の三大要素
  3. ポリモーフィズム

開発環境

  • PHP 7.2

コードにタイプヒンティングを用いている為、PHPのバージョン的には7.0以上ですが、 テーマであるポリモーフィズム自体にはPHPのバージョンは関係ありません。

オブジェクト指向の三大要素

オブジェクト指向の三大要素といえば「クラス(カプセル化)」「ポリモーフィズム」「継承」です。

「クラス」によって、カプセル化が行え独立性の高い部品を作成できるようになりました。

「ポリモーフィズム」によって、メッセージパッシングの共通化を行えるようになりました。

「継承」によって、サブクラスに共通する処理をスーパークラスへ全体集合的にまとめて重複を排除できるようになりました。

ところで、三大要素と言う割には、名称に横文字と漢字が混在していて微妙に気持ち悪いと感じませんか。 なぜ継承も横文字カタカナで紹介しなかったのか。

そこでまずは、名称の言語を揃えてみます。

  • 「分類」「多様性」「継承」
  • 「クラス」「ポリモーフィズム」「インヘリタンス」
  • 「class」「polymorphism」「inheritance」

インヘリタンス...

継承のままでよいかもしれませんね。

ただ、こうして言語を揃えてみると、それぞれの特徴が見えてきます。

クラス=分類=分ける
ある目的の上で分け、独立したまとまりを形成する
ポリモーフィズム=多様性=性質に類似性のある群
類似したクラスに対するメッセージングの共通化
継承=受け継ぐ
共通の処理をまとめ、渡す

なんとなくポリモーフィズムの立ち位置もぼんやり見えてきたでしょうか。

ポリモーフィズム

ポリモーフィズムは簡単に言えば、使用する側を主体として汎用性の高い部品を作る仕組みです。 つまり、利用者が具体的にどのクラスであるかを気にせずにメッセージング出来る仕組みの事を言います。

ここからはコードを書いて見てみます。

今回の登場人物(人物だけではないけど)は、以下の方々です。

  • ドライバー(ヒト科)
  • ガソリン車(鉄+ガソリン)
  • ディーゼル車(鉄+軽油)
  • 電気自動車(鉄+電気)

まずは車を定義します。これが三車種の基となるので「車とはこうあるべき」を宣言します。 つまりこれはインターフェースです。

Car.php
interface Car
{
/* エンジンをかける */
public function start();
}

さすがに全てをやるわけにはいかないので、今回はエンジン(モーター)をスタートさせる部分だけを取り上げます。

では、この「車とはこうあるべき」を基に、三車種を定義します。 このインターフェースを実装する事で、この三車種は晴れて「車である」と認めてもらえることになります。

GasolineCar.php
class GasolineCar implements Car
{
public function start()
{
echo sprintf('%s エンジンスタートしました!', $this->gasolineIgnition());
}

/**
* ガソリンに点火します
* @return string
*/
private function gasolineIgnition(): string
{
return 'ガソリンばーん!';
}
}

ガソリン車はガソリンという燃料を使い、それに最も最適な点火手順を経て、エンジンをスタートさせる事が出来ます。

DieselCar.php
class DieselCar implements Car
{
public function start()
{
echo sprintf('%s エンジンスタートしました!', $this->DieselIgnition());
}

/**
* 軽油に点火します
* @return string
*/
private function DieselIgnition(): string
{
return '軽油ボーン!';
}
}

ディーゼル車も同じです。軽油という燃料を使い、それに最も最適な点火手順(つまりガソリンとは違った手段)でエンジンをスタートさせる事が出来ます。

ElectricCar.php
class ElectricCar implements Car
{
public function start()
{
echo sprintf('%s モータースタートしました!', $this->onElectric());
}

/**
* 電気でモーターを始動させます
* @return string
*/
private function onElectric(): string
{
return '電気ビリビリー!';
}
}

電気自動車に至ってはもはやエンジンではなくモーターです。

それぞれの始動方法は違いますが、同じインターフェースを実装した事で、これらは「車である」と認められました。 つまり、どの車に乗っても、start()というメッセージを送る事で、それぞれを始動させる事ができるわけです。

では次に、エンジンをかける動作を行えるドライバーを定義(創造)します。

driver.php
class driver
{
/**
* 車のエンジンをかけます
* @param Car $car
*/
public function on(Car $car)
{
$car->start();
}
}

on()メソッドの引数はCarオブジェクト、つまり「車である」と認められているオブジェクトであれば、 どんな車であってもこのドライバーによってエンジンを始動する事が出来ます。

実装が完了したので、実際にこれらを利用してみます。

index.php
$driver = new driver();


$driver->on(new GasolineCar());
// => ガソリンばーん! エンジンスタートしました!

$driver->on(new DieselCar());
// => 軽油ボーン! エンジンスタートしました!

$driver->on(new ElectricCar());
// => 電気ビリビリー! モータースタートしました!

これまで構築してきた仕組みによって、 ドライバーは車オブジェクトであればどんな車種であるかを気にする事なく エンジンをスタートさせる事ができています。

これが、ポリモーフィズムです。

まとめ

ポリモーフィズムに限らずクラスや継承もそうですが、 結局のところこれらは、アプリケーションの再利用性とか保守性を向上させる為の仕組みに過ぎません。 つまり、オブジェクト指向プログラミングは目的ではなく、手段です。

私たちはなぜオブジェクト指向で書きたいのでしょうか。それは カプセル化を行えば独立性が高まるし、コードの重複を排除すれば汎用性が高まるから。 つまりは保守や拡張に強いアプリケーションを構築して、無駄な保守を排除し、 手早く拡張し、精神安定性も高めて。 そうやって空いた時間を家族や趣味の時間に当てたい。 そういうことですよね。

クリーンなコードで世界中が平和になりますように。

サンプルコード