PHP アドバンス

PHP OOP - 抽象クラス

1. PHP - 抽象クラスと抽象メソッド

抽象クラス(Abstract Class)とは、少なくとも1つの「抽象メソッド」を含むクラスのことです。抽象メソッド(Abstract Method)は、メソッド名などは宣言されていますが、その具体的な処理(実装)が抽象クラス内には記述されていないメソッドを指します。実際の処理の実装は、そのクラスを継承した子クラス側で行わなければなりません。

抽象クラスの目的は、親クラスで宣言された抽象メソッドを、すべての派生クラス(子クラス)に必ず実装させる(強制する)ことにあります。

抽象クラスまたは抽象メソッドを定義するには、abstract キーワードを使用します。

1.1 実装例

以下の例では、Car という抽象ベースクラスを定義し、それを AudiCitroen クラスで継承しています。

<?php
// 抽象ベースクラス
abstract class Car {
  public $name;

  // 通常のメソッド(抽象ではない)
  public function __construct($name) {
    $this->name = $name;
  }

  // 抽象メソッド - 子クラスに実装を強制する
  abstract public function intro();
}

// 抽象クラスを継承した子クラス
class Audi extends Car {
  public function intro() {
    return "ドイツの品質!私は $this->name です!";
  }
}

// 抽象クラスを継承した子クラス
class Citroen extends Car {
  public function intro() {
    return "フランスの華やかさ!私は $this->name です!";
  }
}

// 子クラスのオブジェクトを作成
$audi = new audi("Audi");
echo $audi->intro();
echo "<br>";

$citroen = new citroen("Citroen");
echo $citroen->intro();
?>

1.2 例の解説

Audi クラスと Citroen クラスは、抽象クラスである Car クラスを継承しています。これにより、継承の仕組みを通じて、AudiCitroenCar クラスの public$name プロパティおよび public__construct() メソッドを利用できます。

これに加えて、intro() は抽象メソッドとして定義されているため、すべての子クラスで必ず実装(具象化)されなければなりません。

2. 抽象クラスに関する留意点

抽象クラスを継承する場合、子クラスのメソッドは以下のルールに従って定義される必要があります。

  • 子クラスのメソッド名は、親クラスの抽象メソッド名と同じであること。
  • アクセス修飾子は、親クラスと同等か、あるいはそれよりも制限が緩いものであること。例えば、抽象メソッドが protected で定義されている場合、子クラスでは protected または public として定義する必要があります(private は不可)。
  • 必須の引数(引数の型と数)は一致している必要があります。ただし、子クラス側で独自のオプショナルな引数を追加することは可能です。

まとめると、抽象クラスから継承する際は以下のルールが適用されます:

  1. 子クラスのメソッドは、親の抽象メソッドと同じ名前で再定義する必要がある。
  2. アクセス修飾子は親と同等か、それ以上に公開されている必要がある。
  3. 必須引数の数は同じである必要がある。ただし、子クラス側で追加の引数(デフォルト値を持つもの)を定義することは許可される。

3. 引数を持つ抽象メソッド

次に、抽象メソッドに引数が定義されている例を見てみましょう。

<?php
abstract class ParentClass {
  // 引数を持つ抽象メソッド
  abstract protected function prefixName($name);
}

class ChildClass extends ParentClass {
  // 親で定義された引数を受け取って実装
  public function prefixName($name) {
    if ($name == "John Doe") {
      $prefix = "Mr.";
    } elseif ($name == "Jane Doe") {
      $prefix = "Mrs.";
    } else {
      $prefix = "";
    }
    return "$prefix $name";
  }
}

$class = new ChildClass;
echo $class->prefixName("John Doe");
echo "<br>";
echo $class->prefixName("Jane Doe");
echo "<br>";
echo $class->prefixName("Baby Doe");
?>

4. 子クラスでのオプショナル引数の追加

以下の例では、子クラス側で、親の抽象メソッドには定義されていない2つのオプショナル引数(デフォルト値付きの引数)を追加しています。

<?php
abstract class ParentClass {
  // 引数を持つ抽象メソッド
  abstract protected function prefixName($name);
}

class ChildClass extends ParentClass {
  // 子クラスでは、親の抽象メソッドにないオプショナル引数を定義できる
  public function prefixName($name, $separator = ".", $greet = "Dear") {
    if ($name == "John Doe") {
      $prefix = "Mr";
    } elseif ($name == "Jane Doe") {
      $prefix = "Mrs";
    } else {
      $prefix = "";
    }
    return "$greet $prefix$separator $name";
  }
}

$class = new ChildClass;
echo $class->prefixName("John Doe");
echo "<br>";
echo $class->prefixName("Jane Doe");
?>