【PHP講座11】クラスを継承して機能を拡張しよう

PHP講座

前回、複数の変数や関数をまとめるクラスを解説した。

また、クラスあくまで枠で、それを実際のデータにしたものオブジェクトということも解説した。

以下がその記事だ。

【PHP講座10】クラス・オブジェクトを利用しよう | Shino’s Mind Archive

さて、今回はこのクラスを拡張していく

既存のクラスに、新たなプロパティやメソッドを追加する継承を解説していこう。

さらに複雑になっていくが、一個一個落ち着いて見ていこう。

スポンサーリンク

継承

前回、サンプルとして商品を表すクラスを作成した。

軽くおさらいしておくと、以下のプロパティ、メソッドを持ったクラスだ。

  • プロパティ
    • 商品名
    • 値段
    • 在庫数
  • メソッド
    • (コンストラクタ)各プロパティに値をセットする
    • 売れた時に在庫数を減らす
    • 仕入れ時に在庫数を増やす
    • 情報を表示する

ここで、商品を絞って本を表したいとしよう。

そうすると、上の情報に追加で以下のプロパティが必要になってくる。

  • 筆者
  • 出版社

また、コンストラクタや情報表示も、この二つのプロパティを追加したものが欲しいだろう。

その他、タイトルと筆者の組だけを表示する処理も欲しいとしよう。

最終的に、以下のようにしたい。

  • プロパティ
    • 商品名
    • 値段
    • 在庫数
    • 筆者
    • 出版社
  • メソッド
    • (コンストラクタ)各プロパティに値をセットする
    • 売れた時に在庫数を減らす
    • 仕入れ時に在庫数を増やす
    • 情報を表示する
    • タイトル、筆者を表示する

赤字が追加するもの青字が変更するものだ。

さて、これを別クラスで定義してもいいのだが、大元の商品クラスをちょっと改変した方が簡単に実現できそうだ。

この、大元のクラスに手を加えて新しいクラスを作ること継承と呼ぶ。

今回の場合、商品クラスを継承し、書籍クラスを作る形になる。

このときの継承元…今回で言えば商品クラスのこと親クラス継承先…書籍クラスのこと子クラスと呼ぶ。

継承のしかた

では、書き方を。

class 子クラス名 extends 親クラス名 {
    // プロパティ
    // メソッド
}

通常のクラスの書き方で子クラスの名前を指定する。

その直後に、extendsを挟んで親クラスを指定すれば、その親クラスの枠を持ったクラスが作れるのだ。

このとき、親クラスに指定できるのは一つだけ

これは単一継承と呼ばれ、そう決められているので覚えておこう。

なお、一つの親クラスから複数の子クラスに継承することは可能だ。

例えば、今回のように商品クラスから書籍クラスを作った後、更に商品クラスを継承して食品クラスを作ることは可能だ。

ただ、商品クラスとその他別のクラスを同時に継承することは不可能、ということになる。

オーバーライド

では、中身を見ていこう。

とはいえ、新しくプロパティやメソッドを追加する場合は、通常のクラスと同じように書けばいい

問題は、元のメソッドに手を加える場合だ。

このときは、まったく同じ名前のメソッドを定義する形になる。

これをオーバーライドと言う。

これをすることで、子クラスのオブジェクトからそのメソッドを呼んだ場合に、オーバーライドしたもの…つまり子クラスで定義した方の内容が実行される

例えば、以下のような場合。

class Sample1 {
    public function show(){
        echo "Sample1の表示";
    }
}

class Sample2 extends Sample1 {
    public function show(){
        echo "Sample2の表示";
    }
}

こうすれば、Sample2としてオブジェクトを作れば、Sample2の表示と出てくる。

このように、同名のメソッド定義でオーバーライドが可能だ。

さて、このときに親クラスの内容を実行したい場合がある。

そのときにも、しっかり書き方が用意されている。

parent::メソッド名();

これまでと書き方が変わるが、このように書けば親メソッドの内容を実行することができる。

そのため、さっきのサンプルを以下のように書き換えると…

class Sample1 {
    public function show(){
        echo "Sample1の表示";
    }
}

class Sample2 extends Sample1 {
    public function show(){
        parent::show();
        echo "Sample2の表示";
    }
}

Sample2のオブジェクトを作れば、Sample1、Sample2両方の文字列が出てくるというわけだ。

なお、ここで$this->メソッド名とすると、再帰と言って自身を呼びだすことになってしまう。

親クラスのメソッドを呼ぶ場合には、必ずparent::メソッド名を使うようにしよう。

ちなみに、親クラスで定義したプロパティは、$this->プロパティ名で問題ない。

ただ、アクセス修飾子がprivateの場合にはアクセスできずエラーとなるので気を付けよう。

アクセスしたい場合は、protectedかpublicとすれば呼び出せる。

子クラス以外から呼ばない場合は、protectedにしておこう。

継承サンプル

では、ここまでの内容を使ったサンプルをお見せしよう。

まずはクラス定義部分。

前回定義したProductクラスを継承し、新たにBookクラスを定義する。

持つ情報は上に示したものだが、間が空いてしまったので再掲しよう。

  • プロパティ
    • 商品名
    • 値段
    • 在庫数
    • 筆者
    • 出版社
  • メソッド
    • (コンストラクタ)各プロパティに値をセットする
    • 売れた時に在庫数を減らす
    • 仕入れ時に在庫数を増やす
    • 情報を表示する
    • タイトル、筆者を表示する

赤字が新規追加青字が変更…つまりオーバーライドするものだ。

これを実際にコーディングすると、以下のようになる。

<?php
    class Product {
        private $name;
        private $price;
        private $stock;

        public function __construct($name, $price, $stock){
            $this->name = $name;
            $this->price = $price;
            $this->stock = $stock;
        }

        public function sold($num){
            if($this->stock > $num){
                $this->stock -= $num;
            }else{
                $this->stock = 0;
            }
        }

        public function arrival($num){
            $this->stock += $num;
        }

        public function showData(){
            echo "名前:" . $this->name . "<br>";
            echo "値段:" . $this->price . "円<br>";
            echo "在庫:" . $this->stock . "<br>";
        }
    }

    class Book extends Product {
        private $author;
        private $publisher;

        public function __construct($name, $price, $stock, $author, $publisher){
            parent::__construct($name, $price, $stock);
            $this->author = $author;
            $this->publisher = $publisher;
        }

        public function showData(){
            parent::showData();
            $this->showBookInfo();
        }

        public function showBookInfo(){
            echo "著者:" . $this->author . "<br>";
            echo "出版社:" . $this->publisher . "<br>";
        }
    }
?>

だんだん長くなってきたが、一個一個は簡単なので順番に見ていこう。

まず、Productクラスは前回通り、何も変更していない。

Bookクラスで、まずProductクラスを継承する。

その後、新規追加となるプロパティを定義している。

そこから二つ、メソッドをオーバーライドしている。

コンストラクタはまず親クラスのものを呼び出し、その後新しく追加したプロパティを初期化

showDataメソッド先に親クラスのものを呼び出し、その後別で定義した本情報を表示するメソッドを呼び出している

最後に、その本情報を表示するshowBookInfoメソッドだ。

これで、Bookクラスが完成した。

後は呼び出し部分。

こちらは、通常のクラスと全く同じになる。

子クラスに書かれているメソッドはそちらが呼ばれ、書かれていないものは親クラスのものが呼ばれる。

試しに、以下のような感じで呼び出してみよう。

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

    $prd = new Book("やさしく分かるPHP", 1500, 5, "山田太郎", "A出版");
    $prd->showData();

    $prd->sold(2);
    $prd->showData();

    $prd->arrival(3);
    $prd->showData();
?>

前回と同じく、二つのファイルが同じディレクトリにあるとし、require_once関数で読み込んでいる。

そして、3行目でオブジェクトの生成だ。

ここで呼び出されるのは、子クラスでオーバーライドしたコンストラクタ

戻ってくるのも、もちろんBookクラスのオブジェクトだ。

showDataメソッドもオーバーライドしているので、著者や出版社まで表示される。

その後、sold、arrivalメソッドは親クラスのものが呼ばれている。

実際にアクセスすると、しっかり数字も変わっているのが確認できるはずだ。

アクセスした結果は、以下のようになる。

名前:やさしく分かるPHP
値段:1500円
在庫:5
著者:山田太郎
出版社:A出版
名前:やさしく分かるPHP
値段:1500円
在庫:3
著者:山田太郎
出版社:A出版
名前:やさしく分かるPHP
値段:1500円
在庫:6
著者:山田太郎
出版社:A出版

おわりに

今回は、基となるクラスに手を加えて新しいクラスを定義する継承について解説した。

本当はもう一つ解説したかったのだが、長くなってしまったのでいったん切ろう。

というわけで、次回はそのもう一つ、抽象クラスについて解説する。

今回の継承も絡んでくるので、確実に進めていこう。

コメント

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