【PHP】Lesson5-7:抽象クラスを理解しよう

ながみえ

一つ前のレッスンでは メソッドのオーバーライド について学習しました。

今回は 抽象クラス について見ていきましょう。

Lesson1:基礎文法編
Lesson2:制御構造編
Lesson3:関数編
Lesson4:データ構造編
Lesson5:クラス

 ・Lesson5-1:クラスを定義しよう
 ・Lesson5-2:コンストラクタを理解しよう
 ・Lesson5-3:アクセス修飾子とカプセル化を理解しよう
 ・Lesson5-4:クラスメンバを理解しよう
 ・Lesson5-5:クラスの継承を理解しよう
 ・Lesson5-6:メソッドのオーバーライドを理解しよう
 ・Lesson5-7:抽象クラスを理解しよう ◁今回はココ
 ・Lesson5-8:インターフェースを理解しよう
 ・Lesson5-9:トレイトを理解しよう
 ・確認問題5-1:モンスター捕獲ゲームを作ろう
 ・確認問題5-2:マインスイーパを作ろう

<<前のページ

学習記事一覧

次のページ>>

抽象クラスとは?

PHPでオブジェクト指向プログラミングを学ぶ中で、「抽象クラス」というキーワードを耳にしたことはありませんか?

抽象クラスは、継承やクラス設計の柔軟性を高めるうえで欠かせない概念です。

この記事では、PHPの抽象クラスの基本構文や抽象メソッドの使い方、継承の具体例を初心者向けにわかりやすく解説します。

これを理解することで、より柔軟で再利用性の高いコード設計ができるようになり、図形クラスなど実践的な練習問題にも挑戦できるようになります。

オブジェクト指向の本質に一歩踏み込むこのテーマを、ぜひこの記事でマスターしていきましょう!

抽象クラスとは?|概要と基本構文の解説

抽象クラス は、子クラスに継承されることを前提とした親クラスです。

具体的な処理内容を持たない 抽象メソッド 持つことができ、newキーワードで直接インスタンス化することはできません。

抽象クラスを定義する際は、abstractキーワード を用いて以下のように書きます。

abstract class 抽象クラス名 {						 // 抽象クラスの定義
    abstract protected function 抽象メソッド名();	// 抽象メソッドの定義
    // 抽象メソッドはメソッドの名前のみ定義し、具体的な動作は不要
}
  1. abstract class で抽象クラスを定義します。
  2. abstractキーワードで抽象メソッドを宣言しますが、処理内容は書きません。
  3. 抽象クラス自体はインスタンス化できません。

このように抽象クラスは「処理の共通枠組み」を提供します。

実際に抽象クラスを使用する例を見てみましょう。

<?php
abstract class Animal {							// 抽象クラスの定義
    abstract protected function makeSound();	// 抽象メソッドの定義
}

class Dog extends Animal {				// 抽象クラスを継承したDogクラスの定義
    protected function makeSound() {	// 抽象メソッドのオーバーライド
        echo "ワンワン" . PHP_EOL;		// このメソッドの具体的な処理内容
    }
}

$dog = new Dog();	// 子クラスのインスタンス生成
$dog->makeSound();
  1. 抽象クラス (Animal): 動物という概念を定義。
  2. 抽象メソッド (makeSound): 具体的な動作は決めずに「音を出す」機能のみ宣言。
  3. 継承クラス (Dog): Animalを継承し、具体的に「ワンワン」と鳴く処理を定義。

このように継承とオーバーライドを使うことで、共通機能を抽象化しつつ、必要に応じて具体化できます。

具体例で学ぶ抽象クラス|活用シーンをわかりやすく解説

もう少し実践的な例を見てみましょう。

<?php
abstract class Animal {							// 抽象クラスの定義
    abstract protected function makeSound();	// 抽象メソッドの定義
    public function eat() {						// 通常のメソッドの定義
        echo "食事中..." . PHP_EOL;
    }
}
class Cat extends Animal {				// 抽象クラスを継承したCatクラスの定義
    protected function makeSound() {	// オーバーライド
        echo "ニャーニャー" . PHP_EOL;
    }
}
class Dog extends Animal {				// 抽象クラスを継承したDogクラスの定義
    protected function makeSound() {	// オーバーライド
        echo "ワンワン" . PHP_EOL;
    }
}

$cat = new Cat();	// インスタンス生成
$dog = new Dog();	// インスタンス生成

$cat->makeSound();	// それぞれの鳴き声を呼び出す
$cat->eat();		// 共通処理を実行
$dog->makeSound();	// それぞれの鳴き声を呼び出す
$dog->eat();		// 共通処理を実行
}

このコードを実行すると以下のように出力されます。

ニャーニャー
食事中...
ワンワン
食事中...
  1. 抽象クラスの活用: 共通機能 (eat) を持たせつつ、各クラスで固有動作 (makeSound) を定義。
  2. 拡張性の向上: 新しい動物クラスを追加するだけで機能を拡張できる。

抽象クラスを継承した子クラスは、抽象メソッドを必ずオーバーライドしないといけません。

抽象クラスは、子クラスに対して抽象メソッドの実装を強制する仕組みです。

これによりコードの再利用性と保守性を向上させます。

まとめ

ここまでで、PHPの抽象クラスについて 概要・基本構文・抽象メソッド・具体的な活用例 を学びました。

これにより、コードの再利用性を高める設計や、複数のクラス間で共通する処理やルールをまとめる方法を理解できたはずです。

抽象クラスの考え方を身につけると、単なる動くコードではなく、保守性が高く、柔軟に拡張できるプログラムが書けるようになります。これは、実務でも大きな武器となるスキルです。

次のステップでは、この知識を活かして、さらに高度なオブジェクト指向の概念へ進んでいきましょう。

あなたのPHPスキルは、着実にプロフェッショナルの領域へ近づいています。

演習問題|図形クラスを使って抽象クラスの仕組みを体験しよう

図形の面積を計算するプログラムを作成しましょう。

抽象クラスを利用して共通のメソッドと機能を定義し、具体的な図形(長方形と円)の面積を計算します。

このプログラムでは抽象クラスとその継承を学びながら、クラスの設計やメソッドのオーバーライドについて理解を深めます。

この演習の要件

以下の要件に従ってコードを完成させてください。

  1. 抽象クラス Shape の定義
    • メソッド getArea() を抽象メソッドとして定義すること。
    • メソッド printArea() を具体的なメソッドとして定義し、面積を表示すること。
  2. Rectangle クラスの定義
    • Shape クラスを継承すること。
    • コンストラクタを定義し、幅 (width) と高さ (height) を初期化すること。
    • 面積を計算する getArea() メソッドを実装すること。
  3. Circle クラスの定義
    • Shape クラスを継承すること。
    • コンストラクタを定義し、半径 (radius) を初期化すること。
    • 面積を計算する getArea() メソッドを実装すること。
  4. 配列に複数の図形を格納して処理
    • 長方形と円のインスタンスを作成すること。
    • 配列に格納し、繰り返し処理で各図形の面積を表示すること。
  5. 出力結果の形式
    • 面積は「この図形の面積は ○○ 平方センチメートルです。」と表示すること。

ただし、以下のような実行結果となること。

この図形の面積は 50 平方センチメートルです。
この図形の面積は 153.94 平方センチメートルです。

解き方のヒント

1からコードを組み立てることが難しい場合は、以下のヒントを開いて参考にしましょう。

Q
ヒント1【コードの構成を見る】

正解のコードは上から順に以下のような構成となっています。
(※下記の□はコード内のインデントを表しています)

1:PHPの開始タグ
2:Shape抽象クラスの定義
  □ 抽象メソッドgetAreaの定義(子クラスで実装される必要がある)
  □ 図形の面積を表示する共通メソッドprintAreaの定義
  □ □ 「この図形の面積は 面積 平方センチメートルです。」と出力
3:Rectangleクラスの定義(Shapeクラスを継承)
  □ プライベート変数$widthと$heightを定義
  □ コンストラクタの定義(引数として幅と高さを受け取り初期化)
  □ 面積を計算するgetAreaメソッドの実装(幅×高さを返す)
4:Circleクラスの定義(Shapeクラスを継承)
  □ プライベート変数$radiusを定義
  □ コンストラクタの定義(引数として半径を受け取り初期化)
  □ 面積を計算するgetAreaメソッドの実装(π×半径²を計算し小数点以下2桁まで丸める)
5:配列$shapesの定義(RectangleとCircleインスタンスを格納)
6:foreachループで$shapes配列の各要素を処理
  □ 各図形のprintAreaメソッドを呼び出して面積を出力

Q
ヒント2【穴埋め問題にする】

以下のコードをコピーし、コメントに従ってコードを完成させて下さい。

<?php
/*
【穴埋め問題1】ここで図形を表す抽象クラスShapeを定義し、抽象メソッドgetAreaを宣言してください。
*/

    // 図形の情報を出力する共通メソッド
    public function printArea() {
        echo "この図形の面積は " . $this->getArea() . " 平方センチメートルです。" . PHP_EOL;
    }
}

// 長方形クラス: Shapeを継承し、具体的な面積計算を実装
class Rectangle extends Shape {
    private $width;  // 幅
    private $height; // 高さ

    // コンストラクタ: 幅と高さを初期化
    public function __construct($width, $height) {
        $this->width = $width;
        $this->height = $height;
    }

    // 面積を計算するメソッドの実装
    /*
    【穴埋め問題2】ここでgetAreaメソッドをオーバーライドし、長方形の面積を計算するコードを書いてください。
    */
}

// 円クラス: Shapeを継承し、具体的な面積計算を実装
class Circle extends Shape {
    private $radius; // 半径

    // コンストラクタ: 半径を初期化
    public function __construct($radius) {
        $this->radius = $radius;
    }

    // 面積を計算するメソッドの実装
    /*
    【穴埋め問題3】ここでgetAreaメソッドをオーバーライドし、円の面積を計算するコードを書いてください。
    */
}

// 長方形と円のインスタンスを作成
$shapes = [
    new Rectangle(5, 10),  // 幅5cm、高さ10cmの長方形
    new Circle(7)          // 半径7cmの円
];

// 各図形の面積を表示
foreach ($shapes as $shape) {
    $shape->printArea();
}

このヒントを見てもまだ回答を導き出すのが難しいと感じる場合は、先に正解のコードと解説を見て内容を理解するようにしましょう。

演習問題の答え合わせ

この問題の正解コードとその解説は以下の通りです。

クリックして開いて確認してください。

Q
正解コード
<?php
// 図形を表す抽象クラス
abstract class Shape {
    // 抽象メソッド: 面積を計算する
    abstract protected function getArea();

    // 図形の情報を出力する共通メソッド
    public function printArea() {
        echo "この図形の面積は " . $this->getArea() . " 平方センチメートルです。" . PHP_EOL;
    }
}

// 長方形クラス: Shapeを継承し、具体的な面積計算を実装
class Rectangle extends Shape {
    private $width;  // 幅
    private $height; // 高さ

    // コンストラクタ: 幅と高さを初期化
    public function __construct($width, $height) {
        $this->width = $width;
        $this->height = $height;
    }

    // 面積を計算するメソッドの実装
    protected function getArea() {
        return $this->width * $this->height;
    }
}

// 円クラス: Shapeを継承し、具体的な面積計算を実装
class Circle extends Shape {
    private $radius; // 半径

    // コンストラクタ: 半径を初期化
    public function __construct($radius) {
        $this->radius = $radius;
    }

    // 面積を計算するメソッドの実装
    protected function getArea() {
        return round(pi() * pow($this->radius, 2), 2); // 小数点以下2桁まで丸める
    }
}

// 長方形と円のインスタンスを作成
$shapes = [
    new Rectangle(5, 10),  // 幅5cm、高さ10cmの長方形
    new Circle(7)          // 半径7cmの円
];

// 各図形の面積を表示
foreach ($shapes as $shape) {
    $shape->printArea();
}
Q
正解コードの解説

コードをブロックごとに分割して解説します。

図形クラスの定義

abstract class Shape {
    abstract protected function getArea(); // 抽象メソッド: 面積を計算する
    public function printArea() {
        echo "この図形の面積は " . $this->getArea() . " 平方センチメートルです。" . PHP_EOL;
    }
}
  1. abstract class Shape:
    • このクラスは抽象クラスとして定義されています。
    • 抽象クラスは直接インスタンス化できませんが、他のクラスに継承されることを目的とします。
  2. abstract protected function getArea():
    • 抽象メソッドは定義だけを行い、具体的な処理は派生クラスで必ず実装しなければなりません。
    • この例では「面積を計算する」処理を各図形クラスで実装することを強制しています。
  3. printArea():
    • 図形の面積を表示する共通機能を提供します。
    • 抽象クラスには具体的なメソッドも含めることができます。

長方形クラスの定義

class Rectangle extends Shape {
    private $width;
    private $height;

    public function __construct($width, $height) {
        $this->width = $width;
        $this->height = $height;
    }

    protected function getArea() {
        return $this->width * $this->height;
    }
}
  • class Rectangle extends Shape:抽象クラスShapeを継承して具体的なクラスを作成します。
  • private $width, $height:クラス内でのみ使用できるプロパティを定義し、データをカプセル化します。
  • __construct($width, $height):コンストラクタを使ってオブジェクトの初期化を行います。
  • getArea():抽象メソッドをオーバーライドして面積計算の具体的な処理を実装します。

円クラスの定義

class Circle extends Shape {
    private $radius;

    public function __construct($radius) {
        $this->radius = $radius;
    }

    protected function getArea() {
        return round(pi() * pow($this->radius, 2), 2);
    }
}
  1. class Circle extends Shape:同じくShapeクラスを継承します。
  2. private $radius:半径を保存するプライベートプロパティを定義します。
  3. __construct($radius):コンストラクタで半径を初期化します。
  4. getArea():
    • 円の面積は π × 半径² で計算します。
    • round(): 小数点以下2桁で丸めます。
    • pi(): 円周率を求めるビルトイン関数。
    • pow(): 指数計算 (2乗)。

インスタンス生成と結果表示

$shapes = [
    new Rectangle(5, 10),
    new Circle(7)
];

foreach ($shapes as $shape) {
    $shape->printArea();
}
  • new Rectangle(5, 10):幅5cm、高さ10cmの長方形を生成します。
  • new Circle(7):半径7cmの円を生成します。
  • foreach ($shapes as $shape):配列に格納されたすべての図形に対して処理を繰り返します。
  • $shape->printArea():共通メソッドprintArea()を呼び出し、それぞれの面積を表示します。
もっと分かりやすい学習サイトにするために

この記事を読んで「ここが分かりにくかった」「ここが難しかった」等の意見を募集しています。

世界一わかりやすいPHP学習サイトにするため、ぜひ 問い合わせフォーム からご意見下さい。

<<前のページ

学習記事一覧

次のページ>>

記事URLをコピーしました