phpにおける継承、抽象化、ポリモーフィズム再確認してみた

phpを使うようになって、perlの頃よりoopを意識するようになった。
せっかくなので今まで曖昧だったoopについて、再確認の意味も込めて書いてみる。


とりあえず書いてみましょう。
初心者なので、つっこみ大歓迎

ピアノを作ってみる

  1 <?php
  2 
  3 class Piano {
  4     private $name = 'piano';
  5     public function play() {
  6         echo "ドレミ";
  7     }
  8 }
  9 
 10 $piano = new Piano();
 11 $piano->play();
 12 
 13 ?>

ピアノクラスを作って見ました。


さて、ここで、ギターも買ったとします。
すると、ピアノクラスとギタークラスで、playという同じメソッドができることになるでしょう。
これはちょいと無駄です。
なわけで、楽器クラスを作り、その楽器クラスを継承してplayというメソッドを使いましょう

継承

  1 <?php
  2 
  3 class Gakki {
  4     private $area= "music";
  5     public function play() {
  6         echo "ドレミ\n";
  7     }
  8 }
  9 
 10 class Piano extends Gakki {
 11     private $name = "piano";
 12 }
 13 
 14 class Guitar extends Gakki {
 15     private $name = "guitar";
 16 }
 17 
 18 $piano = new Piano();
 19 echo "piano : ";
 20 $piano->play();
 21 
 22 $guitar = new Guitar();
 23 echo "guitar : ";
 24 $guitar->play();
 25 
 26 ?>

Gakkiクラスを作って、それを継承してみました。
ところがこのままだと、ピアノもギターも同じ音色です。
こーいつは困った。
というわけで、ピアノを弾いた場合は、「ピアノでドレミ」と出すようにしましょう。

オーバーライド

  1 <?php
  2 
  3 class Gakki {
  4     private $area= "music";
  5     public function play() {
  6         echo "ドレミ\n";
  7     }
  8 }
  9 
 10 class Piano extends Gakki {
 11     private $name = "piano";
 12     public function play() {
 13         echo "ピアノでドレミ\n";
 14     }
 15 }
 16 
 17 class Guitar extends Gakki {
 18     private $name = "guitar";
 19 }
 20 
 21 $piano = new Piano();
 22 echo "piano : ";
 23 $piano->play();
 24 
 25 $guitar = new Guitar();
 26 echo "guitar : ";
 27 $guitar->play();
 28 

このように継承元と同じメソッド名でメソッドを定義し、新たに別の機能を与えることを、オーバーライド。
んでもって、もしオーバーライドされたくなければ、final修飾詞を付ける。
final修飾詞を付ければ、不用意にオーバーライドされることを防げます。


では、ギターを弾いた場合も、「ギターでドレミ」とでるようにしましょう。
しかし、これでは、ピアノもギターもplayメソッドを持ち、楽器クラスでplayを定義する意味がないです。
ただ、今後も楽器が増えることを考えると、「楽器は演奏するもの」という定義は残しておきたい。
こういうときには抽象メソッドという考えを使いましょう。
abstract修飾詞をメソッドの前につけます。
また、抽象メソッドを含むクラスを、抽象クラスと呼ぶようです。

抽象 (abstract)

抽象化メソッドは中身はありません。
ただし、継承元で抽象化メソッドを定義すると、継承先メソッドで必ずそのメソッドがオーバーライドされることを強要します。
サブクラスでオーバーライドされないかぎり、インスタンス化できません

  1 <?php
  2 
  3 abstract class Gakki {
  4     private $area= "music";
  5     protected abstract function play();
  6 }
  7 
  8 class Piano extends Gakki {
  9     private $name = "piano";
 10     public function play() {
 11         echo "ピアノでドレミ\n";
 12     }
 13 }
 14 
 15 class Guitar extends Gakki {
 16     private $name = "guitar";
 17     public function play() {
 18         echo "ギターでドレミ\n";
 19     }
 20 }
 21 
 22 $piano = new Piano();
 23 echo "piano : ";
 24 $piano->play();
 25 
 26 $guitar = new Guitar();
 27 echo "guitar : ";
 28 $guitar->play();
 29 
 30 ?>

ポリモーフィズム

実は、なんとなくポリモーフィズムというものができちゃってます。
昨今は、ポリリズムのほうが有名ですが、
Java初心者です。ポリモーフィズムがよく分かりません。ポリリズムとどう違うんですか?|質問・相談が会員登録不要のQ&AサイトSooda!(ソーダ)
一体ポリモーフィズムとはなんざんしょ。

ポリモーフィズム - Wikipedia

ポリモーフィズムあるいはポリモルフィズム(Polymorphism)とは、プログラミング言語の型システムの性質を表すもので、プログラミング言語の各要素(定数、変数、式、オブジェクト、関数、メソッドなど)についてそれらが複数の型に属することを許すという性質を指す。多態性、多相性、多様性とも呼ばれる。対義語はモノモーフィズム(Monomorphism)、単態性、単相性で、プログラミング言語の各要素が唯一つの型に属するという性質を指す。

わかんねぇ・・。


もう少し噛砕きましょう。
ポリモーフィズムとは同名のメソッドが異なる挙動を示すことを表します。
こちらのサイト様が非常に分かりやすい。
独学PHP はじめよう、PHPでオブジェクト指向

ポリモーフィズムとは同名のメソッドで異なる挙動を実現することをいいます。ポリモーフィズムのメソッドは、同じ目的の機能に同名のメソッドを割り当てることができるということです。これは一連の関連するクラスを利用する場合に、異なるメソッド名を覚えなくていいため、利用者にとって利便性が増します。実装には、単なる継承とオーバーライドだけではそれぞれのサブクラスが同名のメソッドを持つことが必ずしも保証できないため、抽象メソッドという概念をPHPの場合利用します。

つまり、「楽器」には全て「演奏」という機能を「同名メソッド」で与えたい。
それには、「継承」と「オーバーライド」だ。
でも、それだとサブクラスが勝手に、「演奏」以外の独自名でメソッド作っちゃうかも。
それにはphpだと、抽象化が便利だ。
あれ?
これポリモーフィズムじゃね?


ってなわけです。


でも、これにはさらに一個問題を含んでいるらしいです。
それは、phpは多重継承ができないんので、サブクラスが同時に複数のスーパークラスを持てません。
つまり、ポリモーフィズムを実現したいすべてのメソッドを抽象クラスに持たせることに。
サブクラスがスーパークラスの機能を必要としない場合も、オーバーライドしないといけません。。。


わお。冗長。
例えるならなんだろう。
「演奏」の他に、打楽器用の「たたく」と、吹奏楽器用の「吹く」と、指揮用の「ふる」と、歌用の「歌う」メソッドが、すべての楽器サブクラスにあるようなもの?


そんなときに使うのが、interface

interface

インターフェイスとは配下のメソッドがすべて抽象メソッドである特別なクラスを言います

だそうですが、今日は眠いのでまた今度書く

素晴らしく参考にさせていただいたサイト

独学PHP はじめよう、PHPでオブジェクト指向