【PHP講座12】抽象クラスとは?使い方を解説!

PHP講座

前回は、あるクラスを基にして、機能を拡張する継承を解説した。

その際に、既存のメソッドの処理を上書き定義するオーバーライドも解説した。

継承の考え方は今回も使うので、しっかり覚えておこう。

以下が前回の記事だ。

【PHP講座11】クラスを継承して機能を拡張しよう | Shino’s Mind Archive

さて、今回はもう一つ、クラス関連の話をしよう。

新しく、抽象クラスというものを解説する。

前回の継承とはまたちょっと違った考え方なので、その違いに気を付けながら進めていこう。

スポンサーリンク

抽象クラス

では、早速見ていこう。

まず、先に考え方を紹介し、その後書き方の解説、最後に具体例をお見せする。

抽象クラスの考え方

さて、最初に考え方を見ていく。

家庭用ゲーム機を思い浮かべて欲しい。

色々なゲーム機があるが、大体どのゲーム機も、最低限以下の機能は備わっている。

  • ゲームソフトをセットする
  • ゲームソフトをプレイする
  • ゲームの内容をセーブする

最近ではダウンロードソフトもあるので、ゲームソフトのセットがあるか微妙な気もするが、説明のためそれは気にしないことにする。

上に挙げたような内容があるが、ゲーム機によっては実際に行う動作に違いがある

例えば、以下3機種で見ていこう。

  • Nintendo 64
  • ゲームキューブ
  • Switch

このそれぞれで、最初に挙げた3つの動作にどんな違いがあるかを表にしてみよう。

セットプレイセーブ
Nintendo 64ソフトカセットを本体に差し込むプレイするカセット内蔵メモリにセーブする
ゲームキューブソフトディスクを本体に入れるプレイするメモリーカードにセーブする
Switchソフトカードを本体に差し込むプレイする本体のメモリにセーブする
ゲーム機ごとの動作の違い

プレイは共通だが、セットセーブ同じ言葉でも動作が異なる

簡単に言ってしまうと、この言葉側を先に決めてしまうのが、抽象クラスだ。

抽象クラスとは、どんなメソッドを作るかだけの指定が可能になったクラスだ。

メソッド名とその引数だけを定義し、それを継承したクラス側で中身を定義する、というイメージ。

この、名前だけ定義するメソッドのことを抽象メソッドと呼ぶ。

上のゲーム機の例でいえば、ソフトを保持する情報と、プレイするという処理は共通なのでそのまま定義する。

セット、セーブの処理はそれぞれのゲーム機で異なるので、そういう処理が必要だよということをあらかじめ定義しておき、実際に継承した先で具体的な中身を書いていこう、という流れだ。

これができると何が嬉しいかだが、共通する処理を持たせるクラスを複数作る場合に、その呼び出し口を共通で定義できる

また、抽象メソッド実際に継承して作るクラス側で中身の定義が必須になる。

つまり、継承先のクラスで定義してほしいメソッドを決めておくことができるのだ。

定義漏れなどを防ぐことができるので、是非活用していきたい。

抽象クラスの書き方

では、書き方を見ていこう。

まず、クラスの書き方がちょっと変わる。

abstract class 抽象クラス名 {
    // 中身
}

先頭に、abstractという文言がついた形で定義する。

これだけで、そのクラスを抽象クラスとして定義することができる。

プロパティはそのままなので、抽象メソッドの定義方法を。

アクセス修飾子 abstract function 抽象メソッド名(引数);

通常のメソッド定義から、2点変わったところがある。

まず、アクセス修飾子の後ろにabstractがつく。

そして、実際の中身を書かずにセミコロンで終わりだ。

これで、抽象メソッドが定義できた。

なお、ここで中身まで定義することももちろん可能だ。

その場合の動き方は、通常の継承と同じになる。

ここで一つ注意点だが、この抽象クラスそのままオブジェクトにすることはできない

中身がない処理が入っているので、当然といえば当然だろう。

というわけで、これを継承して中身を定義する必要がある。

継承する方法は前回解説した通り、そのままでいける。

抽象クラス具体例

では、具体例を見ていこう。

サンプルの題材は、上に挙げたゲーム機のものを使う。

抽象クラスとしてゲーム機を作り、それを継承して以下3つのクラスを作っていこう。

  • Nintendo 64
  • ゲームキューブ
  • Switch

そして、抽象クラスでは以下の内容を定義する。

  • プロパティ
    • ゲームソフト
  • メソッド
    • ソフトをセットする(abstract)
    • ゲームをプレイする(中身まで定義)
    • データをセーブする(abstract)

ソフトのセット、データのセーブは継承先それぞれで定義することになる。

これを実際に書いたものが、以下だ。

<?php
    abstract class GameHard {
        protected $soft;

        public abstract function set($soft);
        public abstract function save();

        public function play(){
            echo "ソフト「{$this->soft}」を遊んでいます。<br>";
        }
    }

    class Nintendo64 extends GameHard {
        public function set($soft){
            $this->soft = $soft;
            echo "ソフト「{$this->soft}」のカセットを本体にセットしました。<br>"; 
        }

        public function save(){
            echo "ソフト内蔵メモリにデータをセーブします。<br>";
        }
    }

    class GameCube extends GameHard {
        public function set($soft){
            $this->soft = $soft;
            echo "ソフト「{$this->soft}」のディスクを本体にセットしました。<br>";
        }

        public function save(){
            echo "メモリーカードにデータをセーブします。<br>";
        }
    }

    class NintendoSwitch extends GameHard {
        public function set($soft){
            $this->soft = $soft;
            echo "ソフト「{$this->soft}」のカードを本体にセットしました。<br>";
        }

        public function save(){
            echo "本体にデータをセーブします。<br>";
        }
    }
?>

セットとかプレイとか書いていたが、実際には文字列を表示しているだけだ。

上の説明と照らし合わせてもらえば、何をしているかは大丈夫だと思う。

あとは、実際に3つのクラスをオブジェクトにすればOKだ。

いつものように、同ディレクトリに以下ファイルを作ってみる。

<?php
    require_once("./GameSoft.php");

    $soft1 = new Nintendo64();
    $soft1->set("ゼルダの伝説時のオカリナ");
    $soft1->play();
    $soft1->save();

    $soft2 = new GameCube();
    $soft2->set("ゼルダの伝説風のタクト");
    $soft2->play();
    $soft2->save();

    $soft3 = new NintendoSwitch();
    $soft3->set("ゼルダの伝説Breath of the Wild");
    $soft3->play();
    $soft3->save();
?>

…ソフト名は、私がゼル伝好きなだけなので気にしないで欲しい。

さて、これにアクセスすると以下のように表示される。

ソフト「ゼルダの伝説時のオカリナ」のカセットを本体にセットしました。
ソフト「ゼルダの伝説時のオカリナ」を遊んでいます。
ソフト内蔵メモリにデータをセーブします。
ソフト「ゼルダの伝説風のタクト」のディスクを本体にセットしました。
ソフト「ゼルダの伝説風のタクト」を遊んでいます。
メモリーカードにデータをセーブします。
ソフト「ゼルダの伝説Breath of the Wild」のカードを本体にセットしました。
ソフト「ゼルダの伝説Breath of the Wild」を遊んでいます。
本体にデータをセーブします。

初めて学習される方は、なんでこんなことをするんだ、とお思いかもしれない。

ソフトのプロパティとプレイするというメソッドだけ持つクラスを定義しておき、それを継承すれば同じことができるからだ。

まだ小規模なのでメリットの恩恵を受けづらいが、規模が大きくなってくるとメソッドの実装忘れも発生しやすい

そういったときに、この効力を実感できるだろう。

おわりに

今回は、抽象クラスというメソッド名とその引数だけ定義できるクラスを解説した。

まだ効力は実感できていないかもしれないが、是非覚えておいて欲しい。

さて、次回からはついにデータベースとの連携をしていこう。

初回はまずデータベースにアクセスをして、その次からはしばらくSQLについて解説していこうと思う。

Webアプリケーションを作る場合には恐らくここも必須になる。

実を言うとまだクラスで解説しきれていない部分もあるが、それは随時解説を挟んでいこう。

データベースはデータベースでかなり面白いので、楽しみながら進めていこう。

コメント

タイトルとURLをコピーしました