前回のPHPのクラスのその2です。今回は抽象クラスというものを学んでいきます。

PHPのクラス その2

3年以上前に更新されました。情報が古い可能性があります。
更新日 : 2019年08月14日

前回のPHPのクラスのその2です。今回は抽象クラスと継承というものを学んでいきます。

前回作成したクラスの機能の概要

前回はモンスタークラスという設計図を作成し、モンスター同士を攻撃・防御させることができました。モンスタークラスには、名前・ヒットポイント・攻撃力・防御力というデータを持ち、攻撃と防御をメソッドとして実装しました。

前回の機能から追加・改良する

今回はモンスタークラス以外にも作成してみましょう。今回はモンスターの戦いに、人間(勇者?)を参加させてみたいと思います。
ただし、人間をモンスタークラスからインスタンス作成するのは少し間違っている気がします。モンスターと同様に攻撃や防御など、同じようなデータや処理がありますが、人間なりの処理も考えられます。

例えば、勇者は剣を装備しそうです。(笑)勇者が剣を装備すれば攻撃力も上がりそうですね。
このような機能はモンスターにはない機能だと思います。

このように実装したい機能が被るものと、被らないものがインスタンスごとにできる可能性が開発の現場ではよくあります。

抽象クラスと継承クラス

一部機能を被らせて、独自の機能も追加したい場合に抽象クラスが活躍します。
抽象クラスには他にも便利な機能がありますが、PHPの基本を逸脱しますので、今回は省略します。今回の記事でも十分に便利な機能のため、ご紹介します。

抽象クラスとは

前回の記事で作成したモンスタークラスは設計図として作成されました。つまり、具体的なモンスターの名前などを決める前のぼんやりとした設計図です。

これをインスタンス化した時に初めてドラゴンや鬼などの具体的なモンスターとして作成することができました。

モンスタークラスでも十分抽象的で、様々なモンスターを作成するのに役立ちますが、抽象クラスを利用することで、更に汎用的なクラスを作成することができます。

抽象クラスとは、クラスの処理をさらにまとめ、抽象クラスから継承(抽象クラスの内容を受け継いだ)されたクラスを作成することで、抽象クラスの機能をまるっと受け継ぐ事ができるクラスです。

継承クラスとは

継承クラスとは、抽象クラスの機能を受け継いだ(継承した)クラスのことです。

抽象クラスで実装された機能を全て使うことができます。

抽象クラスと継承クラスの記述方法

<?php
// 抽象クラス
abstract class 抽象クラス名(大文字から) {
	// 実装したいメソッドやメンバー変数を記述
}
// 継承クラス
class 継承クラス名 extends 引き継ぎたい抽象クラス名 {
	// 継承クラス固有で実装したいメソッドやメンバー変数などを記述
}

前回のクラスを利用して抽象クラスを作成

クラスのグループ化と説明しましたが、わかりづらいと思うので、前回のモンスタークラスを使いながら、抽象クラスを利用して、勇者クラスも作成してみます。
今回作成する抽象クラスは、以下の画像の黄色い部分です。

抽象クラス概念図
抽象クラス概念図

勇者である人間クラスも、モンスタークラスも今回は戦うことができる仕様で作成したいとします。ここで、先程書いたとおり、人間クラスには剣などの装備をさせたい場合があります。もちろんモンスターはモンスター独自の機能を付け加えることも可能です。

人間クラスとモンスタークラスで一致している戦うという機能(攻撃メソッドと防御メソッド)は、戦う生き物クラスという更に抽象的なクラスとして作成し、人間やモンスターで独自の機能はそれぞれの人間クラス、モンスタークラスで作成していくと、コードがスッキリしてきます。以下が再現したコードとなります。

<?php

// 戦う動物(抽象)クラス
abstract class FightAnimal {
	// 名前変数
	protected $name;
	// ヒットポイント変数
	protected $hitPoint;
	// 攻撃力変数
	protected $attackPoint;
	// 防御力変数
	protected $defensePoint;

	// コンストラクタ
	public function __construct($name, $hitPoint, $attackPoint, $defensePoint) {
		// $thisはインスタンス自身を示します
		$this->name = $name;
		$this->hitPoint = $hitPoint;
		$this->attackPoint = $attackPoint;
		$this->defensePoint = $defensePoint;
	}

	// 攻撃する処理
	// 最後にインスタンスで決められた攻撃力を返却します
	public function attack() {
		echo $this->name . "は相手に" . $this->attackPoint . "ポイントの攻撃を与える!<br>";
		return $this->attackPoint;
	}

	// 防御する処理
	// 引数に敵の攻撃力を入力します
	public function defense($enemyAttackPoint) {
		$damage = $enemyAttackPoint - $this->defensePoint;

		$this->hitPoint -= $damage;
		echo $this->name . "は相手から" . $damage . "ダメージ受けた!<br>";
		echo $this->name . "の現在のヒットポイントは" . $this->hitPoint . "です。<br>";
	}
}

// モンスターの設計図(モンスタークラス)
class Monster extends FightAnimal {

	// モンスターには溜め攻撃を実装
	public function chargeAttack() {
		echo $this->name . "はチャージ攻撃をした!攻撃力が1.1倍の攻撃!<br>";
		$chargeAttackPoint = $this->attackPoint * 1.1;
		echo $this->name . "は相手に" . $chargeAttackPoint . "ポイントのチャージ攻撃を与える!<br>";
		return $chargeAttackPoint;
	}

}

// 人間クラス
class Human extends FightAnimal {
	// 人間クラスでは武器を持つことができる
	private $weaponName;

	// 武器を装備する処理
	// 装備後は引数で受け取った攻撃力を増加させる
	public function equipWeapons($weaponName, $attackPoint) {
		$this->weaponName = $weaponName;
		$this->attackPoint += $attackPoint;
		echo $this->name . "は" . $this->weaponName . "を装備して、攻撃力が" . $attackPoint . "上昇した!<br>";
		echo $this->name . "の攻撃力は現在" . $this->attackPoint . "です。<br>";
	}
}

今回は「戦う動物」という抽象クラスを作成し、そこには名前やヒットポイント・攻撃力・防御力など戦わせる上で必要なメンバー変数を定義し、攻撃する処理と防御する処理を実装しました。

そして、「モンスタークラス」と「人間クラス」では、戦う動物クラスを継承しているため、クラス内に処理を書かなくても攻撃や防御の処理や抽象クラスでのメンバー変数は利用可能になります。

抽象クラスで使用されているpublicやprotectedなどの文字は次回の記事でご説明します。今のところは気にしなくて大丈夫です。

モンスタークラスも人間クラスも、戦うだけでは継承クラスとして作成する意味が無いので、それぞれ独特な機能を実装しています。

ちなみに、抽象クラスではないクラスでも継承することができます。Humanクラスを継承したWitchクラス(魔女クラス)などを作成してみたりも考えられますね。

抽象クラスの注意点としては、抽象クラス(abstractとなっているクラス)は直接インスタンス化することができません。抽象クラスは継承されて初めて使用することができます。

これらを実際にインスタンス作成し、戦う処理をさせたコードが以下になります。

// ドラゴン作成
$dragon = new Monster("ドラゴン", 200, 20, 10);
// 勇者作成
$brave = new Human("勇者", 150, 15, 15);
// 勇者の剣装備
$brave->equipWeapons("勇者の剣", 25);

$dragon->defense($brave->attack());
echo "<br>";
$brave->defense($dragon->chargeAttack());

実行結果は以下の画像のようになります。記事上はコードが分かれていますが、実行時はすべて1ファイルにまとめています。

モンスタークラスと人間クラスの実行結果
モンスタークラスと人間クラスの実行結果

このように継承クラスでは抽象クラスの実装をそのまま利用しつつ、継承クラスで実装されたメソッドやメンバー変数も使用することができます。

まとめ

今回は抽象クラスについて書いていきました。抽象クラスを使えば同じ処理を行うクラスが複数あった場合に何度も同じ処理を書かなくても実装することができます。

抽象クラスは他にも便利に扱う機能がありますが、応用編になるので今回の内容からは除外しました。まずは抽象クラスや継承されたクラスの実装方法を学んでいただければOKです。