【PHP】レッスン5-07:抽象クラスを理解しよう

記事内に商品プロモーションを含む場合があります

この記事で学べる知識:抽象クラス

この記事の練習問題を解くために必要な知識:
基礎文法・制御構造・関数・データ構造(レッスン1~4)クラスの定義と使用コンストラクタアクセス修飾子とカプセル化クラスメンバクラスの継承メソッドのオーバーライド抽象クラスインターフェーストレイト

オブジェクト指向プログラミングでは抽象クラスは基底クラス(親クラス)として機能し、共通の機能やインターフェースを定義するために使われます。

直接インスタンス化できないため、継承を前提としたクラス設計で利用されます。

抽象クラスは次のような特徴を持ちます:

  1. 継承されることを前提に作られるクラス。
  2. abstract キーワードを使って定義する。
  3. 抽象メソッドを含むことができ、派生クラスで具体的に実装される必要がある。

以下では具体的にコードを示しながら詳しく解説します。

<<前のページ PHP記事一覧 次のページ>>

抽象クラスの定義

【初心者向け】PHPのオブジェクト指向を分かりやすくまとめた概念図。 特にクラスの継承、オーバーライド、抽象クラス、インターフェース、トレイトの関係性を視覚的に理解できるようまとめている。

抽象クラスは以下のように定義します。

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

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

抽象メソッドと継承の仕組み

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を継承し、具体的に「ワンワン」と鳴く処理を定義。

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

抽象クラスの具体例と使用シーン

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;
    }
}

// 動物のリストを管理
$animals = [
    new Cat(),
    new Dog()
];

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

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

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

このように抽象クラスはコードの再利用性と保守性を向上させます。

まとめ

抽象クラスは、継承を前提としたクラス設計において、共通機能を定義し、派生クラスに具体的な実装を強制する重要な役割を果たします。

利点

  • コードの再利用性: 共通の処理を1か所にまとめられる。
  • 保守性の向上: 新しいクラスの追加が容易。
  • 設計の明確化: 抽象メソッドにより実装の意図が明示される。

注意点

  • 抽象クラス自体はインスタンス化できません。
  • 継承を利用しない場合は適用する必要はありません。

抽象クラスは設計の柔軟性を高める強力なツールです。

システム設計や大規模開発で特に活躍しますので、ぜひ活用してください。

抽象クラスの練習問題:図形クラスを使って拡張可能なコードを作成しよう!

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

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

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

この問題の要件

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

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

ただし、以下のような実行結果となるコードを書くこと。

*****↓↓正解コードの実行結果の例↓↓*****

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

この問題を解くヒント

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

ヒント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メソッドを呼び出して面積を出力

ヒント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();
}

以上がこの問題の穴埋めコードです。

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



練習問題の解答と解説

この問題の一つの正解例とそのコードの解説を以下に示します。

正解コードの例

例えば以下のようなプログラムが考えられます。

<?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();
}

正解コードの解説

今回のコードは抽象クラスを活用して「長方形」と「円」の面積を計算するプログラムです。

以下では、コードをブロックごとに分けて詳しく解説します。

図形クラスの定義

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()を呼び出し、それぞれの面積を表示します。

まとめ

このコードでは抽象クラスを活用して「長方形」と「円」という異なる図形を扱いながら、それぞれの面積計算を簡潔にまとめました。

  1. 共通の処理をまとめることで、コードの再利用性を高めます。
  2. 抽象メソッドにより、派生クラスで必ず実装すべき機能を強制できます。
  3. 拡張性が高く、新しい図形クラスを追加する際も容易に対応できます。

このプログラムは抽象クラスの基本的な使い方を理解し、実際のシステム設計や拡張に活かすための第一歩となります。

コードを応用し他の図形や計算処理にもチャレンジしてみましょう!

<<前のページ PHP記事一覧 次のページ>>

この記事への質問・コメント

この記事を作成するにあたりAIを活用しています。

問題ないことは確認していますが、もし間違いや表現の違和感などありましたら、ご指摘頂けると大変助かります。






    PHPのテキスト&問題集トップへ戻る
    トップページへ戻る