継承

継承とは

クラスには継承という仕組みが存在します。
継承とは、既に存在するあるクラスを元に、それを更に拡張したクラスを作る仕組みです。
ほとんど似ているけど、ちょっとだけ違うクラス、とかそういうのを作るのに適しています。

継承の仕方は以下のような感じです。

<?php

class クラス名 extends 親クラス名
{

}

?>

通常のクラスと違い、クラス名の後に「extends 継承元のクラス名」を付け加えます。
当然、継承元のクラスは既に存在する必要があります。

継承してクラスを作ると、そのクラスは継承元のクラスのメンバを全て引き継ぎます。
ただし、修飾子がprivateのメンバは引き継がれません
privateはあくまでそのクラス内でのみ参照可能なことを表します。

そこでprotectedという修飾子が存在します。
protectedはそのクラス、および継承先のクラスでのみ参照可能な事を表す修飾子です。外部からは見えません。
なので、継承を想定したクラスでは、非公開メンバは「protected」にしましょう。

継承の利用

継承の例を見てみましょう。

<?php

// Productクラス
class Product
{
    protected $name; // 商品名
    protected $price; // 価格
    
    // コンストラクタ
    public function __construct($name)
    {
        $this->name = $name;
    }

    // 価格を取得
    public function getPrice()
    {
        return $this->price;
    }    

    // 価格を設定
    public function setPrice($price)
    {
        $this->price = $price;
    }
}

// Productクラスを継承したFoodProductクラス
class FoodProduct extends Product
{
}

?>

Productクラスを継承してFoodProductクラスを作る例です。
例のように中身が空の継承クラスを作ると、継承元のクラスとは名前が違うだけのコピーのようなものとなります。

しかし何も書かないでは何の意味もないので、何かメンバを追加しましょう。

<?php

class Product
{
    (略)
}

class FoodProduct extends Product
{
    private $expire; // 賞味期限日数
    
    public function setExpire($expire)
    {
        $this->expire = $expire;
    }

    public function getExpire()
    {
        return $this->expire;
    }
}

?>

Productクラスには商品名と価格のフィールドが存在し、価格用のセッタとゲッタが存在します。
Productクラスを継承したFoodProductクラスですが、食品なので賞味期限フィールドとそのゲッタ・セッタを設けます。
見た目上、FoodProductにはexpireしか存在しないように見えますが、Productクラスを継承しているため、nameやpriceなどもFoodProductのメンバとなります。

<?php

$food = new FoodProduct('肉');

$food->setPrice(100);
echo '価格を' . $food->getPrice() . '円に設定しました。
'
; $food->setExpire(30); echo '賞味期限を' . $food->getExpire() . '日に設定しました。
'
; ?>
価格を100円に設定しました。
賞味期限を30日に設定しました。

FoodProductクラスにはsetExpireとgetExpireメソッドしか記述していませんが、setPriceメソッドやgetPriceメソッドもちゃんと実行できています。

このようにクラスの継承では元々存在するクラスに対して追加機能を持たせた新しいクラスを作る事が出来ます。上の例で言うとProductの機能は全て持った上で新しい機能を持たせたFoodProductというクラスを作成しました。

継承による保守性の確保

プログラミングの鉄則の一つとして「同じ処理は二度書かない」ということが言えると思います。なぜなら、同じ事をやる処理が2箇所で書かれていたら、そこを修正するとき、2箇所とも修正しなければなりません。しかも2箇所に同じコードがコピペで書かれているということは他にもあるのかもしれない、という事になってしまいます。
なので、同じ処理が2箇所で出てくるなら、関数として切り出して、それを呼び出すようにするわけです。
継承の実質的なメリットもこれではないかと思います。

仮に上の例でFoodProductを作るときに、Productを継承するのではなく、コピペでProductクラスをコピーした後、クラス名を変えてメソッドを追加するというような手法を取ると結果的には同じになりますが、もしProductクラスに仕様変更があった場合、全く同じプログラム修正をProductクラスとFoodProductクラスの両方に適用しなければならないことになります。

そういったことを避けるためにも継承を利用するのです。
基本、似たようなクラスを作るのにコピー&ペーストはするべきではないです。

逆の考え方で、元々は何も意識していなかったある二つのクラスで、よく見ると共通な部分が結構ある事に気づいた、というような場合、積極的にこの共通部分を抽出して別クラスとし、これを継承する形にしていくべきです。

オブジェクト指向では継承が便利という事はよく言われますが、便利機能というよりは、保守性の向上の為という意識を持てば、自然と継承という発想が出てくるのではないかと思います。