「PHP言語」の「継承・オーバーライド・抽象クラス」を学んでみた
「オブジェクト指向」には欠かせない「継承」の仕組みついて「PHP言語」で実装する方法を学んでいきたいと思います。
クラスの間に「親」と「子」の関係を作ることができる仕組みですが、
で図解していますので、こちらも見てみてください。
「継承」関係の作り方
継承関係を作る中で、今回は「洗濯機(WashingMachine)」をテーマにしていきたいと思います。
最近の洗濯機は多機能なものも多く、いろいろな洗い方を選択することができますが、どの洗濯機にも
- 洗う(washing)
- 脱水(dehydration)
という機能が付いています。
さらに、どの洗濯機にも「洗濯物の容量」があるので、
- 許容量(capacity)
という項目も作れそうです。
そして、「親」となる「wachingMachine」クラスは、
class WashingMachine { private $capacity = 0; function __construct($capacity_val){ $this->capacity = $capacity_val; } function getCapacity(){ return $this->capacity; } function washing(){ print '「洗濯」を行いました。<br>'; } function dehydration(){ print '「脱水」を行いました。<br>'; } }
のように作成してみました。
洗濯機の製品によってさまざまな機能を持っているものがあり、例えば、
- 人口知能による自動洗濯
- 熱風を利用した乾燥
のような機能を実装しているものもありますが、これらの機能は、すべての洗濯機に搭載されているわけではありません。
なので、これらの機能は「子」クラスに実装を行っていきます。
「親クラス」と「子クラス」の間で「継承関係」を作るためには、
class 子クラス名 extends 親クラス名 { }
のように書いていきます。
例えば、架空のある洗濯機「WsPro」という製品があったと仮定して、これを「子クラス」として継承関係を作ってみたいと思います。
class WsPro extends WashingMachine { function quickDrying(){ print '「急速乾燥」を行いました。<br>'; } }
今回は、この洗濯機に「急速乾燥」機能を追加してみました。
このプログラムを実行するために、
$wspro = new WsPro(4.5); $wspro->washing(); $wspro->dehydration(); $wspro->quickDrying();
のように書いて動かしてみると、
「洗濯」を行いました。 「脱水」を行いました。 「急速乾燥」を行いました。
のように表示されました。
継承した「親」クラスのメソッドを「子」クラスでも呼び出せているのがわかりますね。
ただし、PHPでは「単純継承(親クラスを1つしか持てない)」しかできないようです。
オーバーライド
「親」クラスのメソッドの内容を、「子」クラスで上書きできる機能が「オーバーライド」です。
例えば、
class WsPro extends WashingMachine { function quickDrying(){ print '「急速乾燥」を行いました。<br>'; } //オーバーライドしたメソッド function dehydration(){ print '「急速脱水」を行いました。<br>'; } }
のように、「親」クラスと同じ「メソッド名・引数名のメソッド」を作ると、「オーバーライド」することができます。
先ほどと同じプログラムを実行すると、
「洗濯」を行いました。 「急速脱水」を行いました。 「急速乾燥」を行いました。
のように「脱水」という文字が、「急速脱水」という文字に変化しているのがわかりますね。
オーバーライドを禁止する方法
どのような「親」クラスのメソッドでも「オーバーライド」ができてしまうと、意図しない方法でメソッドが利用されてしまうことも考えられます。
そのため、オーバーライドされたくないメソッドには「final」を付けることで、「オーバライドできないメソッド」を作ることができます。
今回は、「親」クラスに「Countries」というクラスを作成し、このクラスを継承する「子」クラスに「Country」クラスを作りました。
まず、「Countries」クラスは、
class Countries { private $count = 196; public final function getCount(){ return $this->count; } }
のように作成し、全世界の国の数を格納している「count」プロパティとゲッターの「getCount」メソッドを作成しました。
もし、
class Country extends Countries { //オーバーライドしたメソッド function getCount(){ return $count; } }
のように、間違って「getCount」メソッドをオーバーライドしてしまい、
$country = new Country(); $country->getCount();
のように実行してしまうと、
Fatal error: Cannot override final method Countries::getCount()
のようにエラーが発生します。
定義したメソッドが継承先の「子」クラスでオーバーライドされて困る場合は、必ずメソッド名の前に「final」キーワードを付けることが大切です。
抽象クラス
「抽象クラス」は、メソッドの実装内容は作らずに「メソッド名や引数のみ」を宣言しておき、継承先で実装内容を作るクラスです。
「抽象クラス」を作るためには、
abstract クラス名{ // メソッド名や引数のみを宣言 abstract アクセス修飾子 function メソッド名(引数); }
という形で、クラス・メソッド名の前に「abstract」キーワードを付けると「抽象クラス」を作ることができます。
今回は、
- 親クラス(抽象クラス)
- 「紙」を表す「Paper」クラス
- 子クラス(抽象クラスの継承先)
- 「本」を表す「Book」クラス
のように作っていきたいと思います。
初めに抽象クラスとなる「Paper」クラスは、
abstract class Paper { private $weight; private $material; function __construct($weight, $material){ $this->weight = $weight; $this->material = $material; } public static function getWeight(){ return $this->count; } public static function getMaterial(){ return $this->count; } abstract public function usePaper(); }
のように作ってみました。
紙の「重量(weight)」と「素材(material)」のプロパティを作り、コンストラクタで値を設定しています。
そして、「usePaper」というabstractなメソッドを作成しています。
紙の利用方法は用途によってさまざまななので、このように作ってみました。
そして、「本」を表すクラスは、
class Book extends Paper { private $name = ''; function __construct($name,$weight, $material){ parent::__construct($weight, $material); $this->name = $name; } public function usePaper() { print '紙に本の内容を印字しました。'; } }
のように作成しています。
コンストラクタのなかにある「parent::」の部分は「親」クラスのメソッドを呼び出すときに指定するキーワードです。
「weight」と「material」はそのまま親クラスに渡して、プロパティに値を設定してもらうことができるので、便利ですね。
そして、「usePaper」の内容の実装も忘れずに行っておきます。
このクラスを利用したプログラムを、
$book = new Book('プログラミングの教科書', '240', '化学パルプ'); print '紙の材質は、' . $book->getMaterial() . '紙の重量は、' . $book->getWeight() . 'です。'; $book->usePaper();
のように作りました。このプログラムを実行すると、
紙の材質は、化学パルプ紙の重量は、240です。紙に本の内容を印字しました。
のように表示されます、
「抽象クラス」は、直接インスタンスを作ることができないため、もし、間違ってインスタンスを作ろうとして、
$paper = new Paper(4.5, 'パルプ');
のように書いてしまうと、
Fatal error: Uncaught Error: Cannot instantiate abstract class Paper
のようにエラーが出てしまいます。
そして、継承先の「子」クラスでも、「abstract」なメソッドの内容を実装しないとインスタンスを作ることができません。
比較的大規模なクラス構造になると必要になるそうなので、少しずつ勉強しながら仕組みを理解していきたいと思います。