オブジェクト指向のプログラミング言語にはクラスの継承というものがあります。
「継承」と聞くと何かを引き継ぐようなイメージをしますよね?
まさにそうなんですが、リレーのバトンのようなものでは無く、もっと厳密に親子の関係に近い状態で引継ぎを行います。
この継承について具体的に書き方や使い方などを説明していきます。
継承とは
クラスの継承とは、既存のクラスのインスタンス変数やメソッドを引き継ぎ新たなクラスを作成するようなものです。
継承元のベースとなるクラスを親クラスで、新たなクラスを子クラスと言います。
子クラスは親クラスの機能を引き継ぎますが、新たに変数やメソッドを追加することも出来ます。
そして、親クラスのメソッドを上書きして処理内容を書き換えることも出来ます。
例えば、水を貯めるだけのポットクラスがあったとき、ポットクラスを継承して電気ポットクラスを作成したり出来ます。
水を貯める機能はそのままに、新たに沸騰させる処理を追加したり出来るという具合です。
人間には親は父と母の二人いますが、プログラミングの継承は親は1クラスだけです。
例えば動物クラスを継承した人間クラスと猫クラスがあったとします。
田中くんクラスは人間クラスを継承して作成しますが、猫クラスも継承してたら不自然でしょう?笑
上の説明分かりづらいかな?これも先輩から聞いた話で僕はなんとなく納得出来たので書いておきました。
継承の書き方
継承を使う場合は extends を使います。
書き方はクラスを定義したときにクラス名の後ろに extends 親クラス と書くだけです。
ポットクラス(Pot)を継承し、電気ポットクラス(ElePot)を作成する場合だと下のようになります。
1 2 3 |
require_once("Pot.php"); class ElePot extends Pot { } |
親クラスとなる Pot は、別ファイルに定義されているため require_once() を使って読み込む必要があります。
親クラスのPotは下の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class Pot { protected $water = 0; function __construct($water) { $this->water = $water; } public function addWater($water) { $this->water += $water; } public function getWater() { return $this->water; } } |
この状態で、子クラスをインスタンス化して実行すると親クラスのメソッドなども実行出来ます。
1 2 3 4 5 |
require_once("ElePot.php"); $pot = new ElePot(20); $pot->addWater(100); echo "現在の水の量は、".$pot->getWater()."です。"; |
このとき親クラスのメソッドのアクセス修飾子が public だから呼び出せるのです。
private ならば呼び出せません。
親メンバを呼び出す
メンバとはインスタンス変数やメソッドのことです。
呼び出せるのはアクセス修飾子が public のものか protected のものです。
子クラスからも親クラスで定義したインスタンス変数やメソッドを参照することが出来ます。
インスタンス変数の場合は、自クラスのインスタンス変数を使うときと同じように書きます。
例えば親クラスに定義したインスタンス変数 protected $water = 0; は、親クラスでも子クラスでも $this->water のよう書き参照できます。
メソッドの場合も、同様です。
しかし次でも説明しますが、同じ名前のメソッドを定義した場合はオーバーライドと言って上書きが出来ます。
その場合は親クラスと子クラスに同じ名前のメソッドが存在することになります。
この呼び出し方に違いが出てきます。
例えば、子クラスからオーバーライドした子クラスの addWater() を実行する場合は、
1 |
$this->addWater($water); |
のように書きます。
子クラスから親クラスの addWater() を実行する場合は、
1 |
parent::addWater($water); |
のように書きます。
もっと具体的なサンプルは次のオーバーライドで説明します。
親メソッドをオーバーライドする
PotクラスのaddWater() は引数で受け取った水を既存のインスタンス変数の水に追加するだけのメソッドでした。
1 2 3 |
public function addWater($water) { $this->water += $water; } |
Potクラスを継承した ElePot クラスでは、水を追加したら同時に水を加熱する処理を行いたいと思います。
その場合は子クラス(ElePot)の方で親クラスに存在する addWater() と同じ名前で同じ引数のメソッドを定義し、新たな処理を定義します。
これをオーバーライドと言います。
ElePot クラスに処理を追加したサンプルが以下です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
require_once("Pot.php"); class ElePot extends Pot { public $heat; public function addWater($water) { parent::addWater($water); $this->heat = 4; $this->doHeat(); } public function doHeat() { if($this->water > 0) { $this->heat = 100; $this->water *= 0.9; } } } |
doHeat()は水を加熱して温度($heat)を100にし、その際に水が1割蒸発するから0.9掛ける処理をしています。
addWater()は親クラスのものをオーバーライドしたメソッドです。
parent::addWater($water); は、親クラスの方の addWater() を実行し、水を追加する処理を書いています。
$this->heat = 4; は、水を足すとポット内の水の温度が下がるので仮に4を設定しています。
$this->doHeat(); は、水を温めるメソッドを実行しています。
例えばこういうとき使ったな
クラスの継承や、継承したときのオーバーライドなど説明しましたが、こんなん実際に使うときある??って思う人もいるかと思います。
実際のプログラミングでポットクラスとか使わないですし。
なので、ちょっとだけ具体的な事例を説明します。
具体的な話で専門用語も多くなりますが、まだ勉強を始めたばかりの方はブックマークしておいて慣れてきたときに読み直してみてください。
まず、WEBサイトを作るときはMVCに役割を分けてコードを書いていきます。
V(ビュー)はほぼHTMLなのであまりクラスを書くことはないです。
M(モデル)はDBにアクセスするためのクラスを定義します。
このとき、DBにアクセスするために必要な処理が毎回必要になります。
また、DBにアクセスするときに不正な文字が混ざっていないかチェックする処理も共通に必要になります。
このとき、DBアクセスクラスを親クラスとして、各DBのテーブルごとに子クラスを作成すると、上で紹介したような処理は全て親クラスで定義しておけば子クラスには書く必要は無くなります。
テーブルが30個くらいあった場合、モデルも30個作成すると、上で紹介した共通処理に不具合が出たときに30ファイル修正が必要になる可能性があります。
しかし、親子の関係で親クラスに共通処理を書いていればそこだけの修正で全て対応でき、テストの範囲も狭まります。
C(コントローラー)もクラスを定義することが多いので同じようなことが言えます。
これは使用するフレームワークによっても異なりますが、CakePHPというフレームワークではこういう使い方をしています。
まとめ
PHPはオブジェクト指向のプログラミング言語です。
そのオブジェクト指向にはクラスの継承というものがあります。
クラスの継承は子クラスのクラス名の後ろに extends を使い継承する親クラス名を定義します。
継承すると子クラスから親クラスの public もしくは、protected のメンバを参照することが出来ます。
メソッドの処理を使用したり、処理内用全て上書きしたりできます。
PHPのフレームワークを使うとこの継承を使う場面が多くなりますので覚えておきましょう。
コメントを残す